Como ignorar a solicitação de comprovação OPÇÕES?

93

Eu desenvolvi um aplicativo PhoneGap que agora está sendo transformado em um site móvel. Tudo funciona bem, exceto uma pequena falha. Eu uso uma determinada API de terceiros por meio de uma solicitação POST, que funciona bem no aplicativo, mas falha na versão do site móvel.

Depois de um olhar mais atento, parece que o AngularJS (na verdade, acho que o navegador) está enviando primeiro uma solicitação OPTIONS. Aprendi muito hoje sobre o CORS, mas não consigo descobrir como desativá-lo completamente. Não tenho acesso a essa API (portanto, alterações nesse lado são impossíveis), mas eles adicionaram o domínio no qual estou trabalhando ao cabeçalho Access-Control-Allow-Origin.

Este é o código do qual estou falando:

        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);
        });

Como posso evitar que o navegador (ou AngularJS) envie essa solicitação OPTIONS e pule para a solicitação POST real? Estou usando o AngularJS 1.2.0.

Desde já, obrigado.

Bram Vandewalle
fonte

Respostas:

100

O preflight está sendo acionado por seu tipo de conteúdo de application/json. A maneira mais simples de evitar isso é definir o Content-Type para ser o text/plainseu caso. application/x-www-form-urlencoded& multipart/form-dataContent-Types também são aceitáveis, mas é claro que você precisará formatar sua carga útil de solicitação de forma adequada.

Se você ainda estiver vendo um preflight após fazer essa alteração, o Angular pode estar adicionando um cabeçalho X à solicitação também.

Ou você pode ter cabeçalhos (Autorização, Controle de Cache ...) que irão acioná-lo, consulte:

Ray Nicholus
fonte
9
Esta é a resposta correta - seus cabeçalhos Content-Type e Cache-Control estão acionando uma solicitação de comprovação. Um GET simples com um Content-Type de text / plain e alguns outros são as únicas maneiras de acionar uma solicitação não preflight. Veja: developer.mozilla.org/en-US/docs/HTTP/…
Jeff Hubbard,
Ah sim, esqueci o Cache-Control.
Ray Nicholus
15
Um cabeçalho personalizado também acionará o preflight.
Sébastien Deprez
1
Alterar o tipo de conteúdo para evitar o teste OPTIONs não é a resposta. O tipo de conteúdo deve corresponder ao tipo de conteúdo independentemente
ekerner
se for o navegador lançando e no backend, o método Http OPTIONS estiver bloqueado, terá algum efeito, se o navegador não chamar a API correspondente para POST / PUT porque OPTIONS falhou?
P Satish Patro
16

Como disse Ray, você pode pará-lo modificando o cabeçalho do conteúdo, como -

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

Por exemplo -

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'}
        });
    }]);

Ou diretamente para uma chamada -

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

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

Isso não enviará nenhuma solicitação de opção pré-voo.

NOTA: A solicitação não deve ter nenhum parâmetro de cabeçalho personalizado. Se o cabeçalho da solicitação contiver qualquer cabeçalho personalizado, o navegador fará uma solicitação pré-voo, você não pode evitá-la.

vivex
fonte
1
Como devo fazer isso apenas com $http?
learnercys
Obrigado, isso é semelhante ao que eu estava fazendo. As únicas mudanças são o método GETe um cabeçalho extra Authorization. Mas ainda enviando o pré-vôo.
learnercys
1
Você pode colar sua solicitação aqui? como onda ou algo assim? Talvez seja por causa do cabeçalho de autorização, tente removê-lo e tente. Se você estiver enviando cabeçalhos personalizados, o angular enviará a solicitação pré-voo.
vivex
pastebin.com/vRDeFiH2 O primeiro é o pedido para $ http e o segundo é um pedido válido compilá-lo pelo carteiro :)
learnercys
2

Ao executar certos tipos de solicitações AJAX de domínio cruzado, os navegadores modernos que suportam CORS irão inserir uma solicitação extra de "comprovação" para determinar se eles têm permissão para executar a ação. Da consulta de exemplo:

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

Como resultado deste fragmento, podemos ver que ao endereço foram enviadas duas solicitações (OPTIONS e GET). A resposta do servidor inclui cabeçalhos que confirmam a permissibilidade da consulta GET. Se o seu servidor não estiver configurado para processar uma solicitação OPTIONS corretamente, as solicitações do cliente falharão. Por exemplo:

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.Void
fonte
2

Acho que a melhor maneira é verificar se o pedido é do tipo "OPÇÕES" e retornar 200 do middleware. Funcionou para mim

express.use('*',(req,res,next) =>{
      if (req.method == "OPTIONS") {
        res.status(200);
        res.send();
      }else{
        next();
      }
    });
Devbrat Raghuvanshi
fonte
Funciona, mas no OWASP é recomendado não expor OPTIONS. Você bloqueia o tempo no serviço de back-end / hospedado (Nginx, Apache) etc.
P Satish Patro
0

Preflight é um recurso de segurança da web implementado pelo navegador. Para o Chrome, você pode desativar toda a segurança da web adicionando a sinalização --disable-web-security.

Por exemplo: "C: \ Arquivos de programas \ Google \ Chrome \ Application \ chrome.exe" --disable-web-security --user-data-dir = "C: \ newChromeSettingsWithoutSecurity". Você pode primeiro criar um novo atalho de cromo, ir para suas propriedades e alterar o alvo como acima. Isso deve ajudar!

Arijit Patra
fonte
Você realmente não pode esperar que OP diga a seus clientes para desligar a segurança do navegador apenas para habilitar um recurso, certo ?!
svarog
@svarog isso é principalmente para propósitos de desenvolvimento, principalmente no servidor de produção, você não enfrentará esse problema.
Arijit Patra
se for o navegador jogando, e no backend, o método Http OPTIONS estiver bloqueado, terá algum efeito, se o navegador não chamar a API correspondente para POST / PUT porque OPTIONS falhou?
P Satish Patro
-2

definir o tipo de conteúdo como indefinido faria o javascript passar os dados do cabeçalho como estão, e sobrescrever as configurações de cabeçalho $ httpProvider padrão do angular. Documentação Angular $ http

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