¿Cómo omitir la solicitud de verificación previa de OPCIONES?

93

Había desarrollado una aplicación PhoneGap que ahora se está transformando en un sitio web móvil. Todo funciona sin problemas, además de un pequeño error. Utilizo cierta API de terceros a través de una solicitud POST, que funciona bien en la aplicación, pero falla en la versión del sitio web móvil.

Después de una mirada más cercana, parece que AngularJS (supongo que el navegador en realidad) está enviando primero una solicitud de OPCIONES. Hoy aprendí mucho sobre CORS, pero parece que no puedo averiguar cómo desactivarlo por completo. No tengo acceso a esa API (por lo que los cambios en ese lado son imposibles), pero agregaron el dominio en el que estoy trabajando a su encabezado Access-Control-Allow-Origin.

Este es el código del que estoy hablando:

        var request = {
                language: 'fr',
                barcodes: [
                    {
                        barcode: 'somebarcode',
                        description: 'Description goes here'
                    }
                ]
            };
        }
        var config = {
            headers: { 
                'Cache-Control': 'no-cache',
                'Content-Type': 'application/json'
            }
        };
        $http.post('http://somedomain.be/trackinginfo', request, config).success(function(data, status) {
            callback(undefined, data);
        }).error(function(data, status) {
            var err = new Error('Error message');
            err.status = status;
            callback(err);
        });

¿Cómo puedo evitar que el navegador (o AngularJS) envíe esa solicitud de OPCIONES y simplemente salte a la solicitud POST real? Estoy usando AngularJS 1.2.0.

Gracias por adelantado.

Bram Vandewalle
fuente

Respuestas:

100

El tipo de contenido de application/json. La forma más sencilla de evitar esto es establecer el tipo de contenido text/plainen su caso. application/x-www-form-urlencoded& multipart/form-dataContent-Types también son aceptables, pero, por supuesto, deberá formatear la carga útil de su solicitud de manera adecuada.

Si sigue viendo una verificación previa después de realizar este cambio, es posible que Angular también esté agregando un encabezado X a la solicitud.

O puede tener encabezados (Autorización, Cache-Control ...) que lo activarán, consulte:

Ray Nicholus
fuente
9
Esta es la respuesta correcta: sus encabezados Content-Type y Cache-Control están activando una solicitud de verificación previa. Un GET simple con un tipo de contenido de texto / simple y algunas otras son las únicas formas de activar una solicitud sin verificación previa. Ver: developer.mozilla.org/en-US/docs/HTTP/…
Jeff Hubbard
Ah, sí, me olvidé de Cache-Control.
Ray Nicholus
17
Un encabezado personalizado también activará la verificación previa.
Sébastien Deprez
2
Cambiar el tipo de contenido para evitar la prueba de OPCIONES no es la respuesta. El tipo de contenido debe coincidir con el tipo de contenido independientemente
ekerner
si el navegador lanza, y en el backend, el método Http OPTIONS está bloqueado, ¿tendrá algún efecto como que el navegador no llamará a la API correspondiente para POST / PUT ya que las OPCIONES fallaron?
P Satish Patro
16

Como dijo Ray, puede detenerlo modificando el encabezado de contenido como:

 $http.defaults.headers.post["Content-Type"] = "text/plain";

Por ejemplo -

angular.module('myApp').factory('User', ['$resource','$http',
    function($resource,$http){
        $http.defaults.headers.post["Content-Type"] = "text/plain";
        return $resource(API_ENGINE_URL+'user/:userId', {}, {
            query: {method:'GET', params:{userId:'users'}, isArray:true},
            getLoggedIn:{method:'GET'}
        });
    }]);

O directamente a una llamada -

var req = {
 method: 'POST',
 url: 'http://example.com',
 headers: {
   'Content-Type': 'text/plain'
 },
 data: { test: 'test' }
}

$http(req).then(function(){...}, function(){...});

Esto no enviará ninguna solicitud de opción previa al vuelo.

NOTA: La solicitud no debe tener ningún parámetro de encabezado personalizado. Si el encabezado de la solicitud contiene un encabezado personalizado, el navegador realizará una solicitud previa al vuelo, no puede evitarlo.

vivex
fuente
1
¿Cómo debo hacerlo solo con $http?
Learnercys
Gracias, eso es similar a lo que estaba haciendo. Los únicos cambios son el método GETy un encabezado adicional Authorization. Pero aún enviando la verificación previa.
Learnercys
1
¿Puedes pegar tu solicitud aquí? como rizo o algo así? Tal vez sea debido al encabezado de Autorización, intente eliminarlo y luego intente. Si está enviando encabezados personalizados, angular enviará una solicitud previa al vuelo.
vivex
pastebin.com/vRDeFiH2 La primera es la solicitud a $ http y la segunda es una solicitud válida
compilada
2

Al realizar ciertos tipos de solicitudes AJAX entre dominios, los navegadores modernos que admiten CORS insertarán una solicitud adicional de "verificación previa" para determinar si tienen permiso para realizar la acción. De consulta de ejemplo:

$http.get( ‘https://example.com/api/v1/users/’ +userId,
  {params:{
           apiKey:’34d1e55e4b02e56a67b0b66’
          }
  } 
);

Como resultado de este fragmento podemos ver que a la dirección se le enviaron dos solicitudes (OPTIONS y GET). La respuesta del servidor incluye encabezados que confirman la permisibilidad de la consulta GET. Si su servidor no está configurado para procesar una solicitud de OPCIONES correctamente, las solicitudes de cliente fallarán. Por ejemplo:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: accept, origin, x-requested-with, content-type
Access-Control-Allow-Methods: DELETE
Access-Control-Allow-Methods: OPTIONS
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Methods: GET
Access-Control-Allow-Methods: POST
Access-Control-Allow-Orgin: *
Access-Control-Max-Age: 172800
Allow: PUT
Allow: OPTIONS
Allow: POST
Allow: DELETE
Allow: GET
M. Vacío
fuente
2

Creo que la mejor manera es verificar si la solicitud es del tipo "OPCIONES" y devolver 200 de middleware. Funcionó para mí.

express.use('*',(req,res,next) =>{
      if (req.method == "OPTIONS") {
        res.status(200);
        res.send();
      }else{
        next();
      }
    });
Devbrat Raghuvanshi
fuente
Funciona pero en OWASP se recomienda no exponer OPCIONES. A resistir a bloquearlo en el servicio backend / alojado (Nginx, Apache), etc
P Satish Patro
0

Preflight es una función de seguridad web implementada por el navegador. Para Chrome, puede deshabilitar toda la seguridad web agregando la marca --disable-web-security.

Por ejemplo: "C: \ Archivos de programa \ Google \ Chrome \ Application \ chrome.exe" --disable-web-security --user-data-dir = "C: \ newChromeSettingsWithoutSecurity". Primero puede crear un nuevo acceso directo de Chrome, ir a sus propiedades y cambiar el objetivo como se indicó anteriormente. ¡Esto debería ayudar!

Arijit Patra
fuente
Realmente no puede esperar que OP le diga a sus clientes que desactiven la seguridad del navegador solo para habilitar una función, ¿verdad?
svarog
@svarog, esto es principalmente para fines de desarrollo, principalmente en el servidor de producción, no enfrentará este problema.
Arijit Patra
si el navegador lanza, y en el backend, el método Http OPTIONS está bloqueado, ¿tendrá algún efecto como que el navegador no llamará a la API correspondiente para POST / PUT ya que las OPCIONES fallaron?
P Satish Patro
-2

establecer el tipo de contenido en indefinido haría que javascript pasara los datos del encabezado tal como están, y sobre escribir las configuraciones de encabezado angular $ httpProvider predeterminadas. Documentación de Angular $ http

$http({url:url,method:"POST", headers:{'Content-Type':undefined}).then(success,failure);
Theophilus Omoregbee
fuente