AJAX no Chrome enviando OPÇÕES em vez de GET / POST / PUT / DELETE?

107

Estou trabalhando em um aplicativo da web interno no trabalho. No IE10, as solicitações funcionam bem, mas no Chrome todas as solicitações AJAX (que existem muitas) são enviadas usando OPTIONS em vez de qualquer método definido que eu forneça. Tecnicamente, minhas solicitações são de "domínio cruzado". O site é servido em localhost: 6120 e o serviço para o qual estou fazendo solicitações AJAX está em 57124. Esse bug fechado do jquery define o problema, mas não é uma correção real.

O que posso fazer para usar o método http adequado em solicitações ajax?

Editar:

Isso está no carregamento do documento de cada página:

jQuery.support.cors = true;

E todo AJAX é construído de forma semelhante:

var url = 'http://localhost:57124/My/Rest/Call';
$.ajax({
    url: url,
    dataType: "json",
    data: json,
    async: true,
    cache: false,
    timeout: 30000,
    headers: { "x-li-format": "json", "X-UserName": userName },
    success: function (data) {
        // my success stuff
    },
    error: function (request, status, error) {
        // my error stuff
    },
    type: "POST"
});
Corey Ogburn
fonte
2
O último comentário nesse relatório de bug explica muito bem ...
Kevin B
1
Isso mudou minha mente porque tudo que estou fazendo é tão simples (e meu código é semelhante ao do bug jquery). Tirando isso, não é desculpa para não incluí-lo. BRB, pegando algum código de amostra.
Corey Ogburn
3
Observe que o IE não considera os números de porta ao determinar se uma solicitação é de origem cruzada.
Ray Nicholus
@KevinB: Nosso serviço REST tira proveito de diferentes solicitações ao fazer coisas diferentes com base no método http. Mudar tudo para GET não é uma solução válida. Além disso, de acordo com a resposta do Dark Falcon, não vai ajudar de qualquer maneira porque eu tenho X-UserName e outros cabeçalhos personalizados nas solicitações.
Corey Ogburn
isso não muda o fato de que, se você deseja fazer uma solicitação de origem cruzada, deve seguir todas as regras aplicáveis ​​às solicitações de origem cruzada para que funcione corretamente. solicitações de origem cruzada geralmente envolvem uma solicitação OPTIONS. Manuseie-o adequadamente e o problema desaparecerá. A única outra maneira de resolver isso (sem alterar a api) é ter um script no mesmo servidor da página primária que interage com a api usando código do lado do servidor.
Kevin B de

Respostas:

136

O Chrome está testando a solicitação para procurar cabeçalhos CORS . Se a solicitação for aceitável, ele enviará a solicitação real. Se estiver fazendo isso entre domínios, você simplesmente terá que lidar com isso ou então encontrar uma maneira de tornar a solicitação não entre domínios. É por isso que o bug do jQuery foi fechado como não corrigido. Isso ocorre por design.

Ao contrário das solicitações simples (discutidas acima), as solicitações "preflighted" primeiro enviam uma solicitação HTTP pelo método OPTIONS para o recurso no outro domínio, a fim de determinar se a solicitação real é segura para envio. As solicitações entre sites são verificadas dessa forma, pois podem ter implicações nos dados do usuário. Em particular, uma solicitação é testada se:

  • Ele usa métodos diferentes de GET, HEAD ou POST. Além disso, se POST for usado para enviar dados de solicitação com um tipo de conteúdo diferente de application / x-www-form-urlencoded, multipart / form-data ou text / plain, por exemplo, se a solicitação POST enviar uma carga XML para o servidor usando application / xml ou text / xml, a solicitação é testada.
  • Ele define cabeçalhos personalizados na solicitação (por exemplo, a solicitação usa um cabeçalho como X-PINGOTHER)
Dark Falcon
fonte
20
Cabeçalhos personalizados. Provavelmente é isso que está desencadeando as chamadas OPTIONS do preflight.
Corey Ogburn
18

Com base no fato de que a solicitação não é enviada na porta padrão 80/443, esta chamada Ajax é considerada automaticamente uma solicitação de recurso de origem cruzada (CORS) , o que em outras palavras significa que a solicitação emite automaticamente uma solicitação OPTIONS que verifica Cabeçalhos CORS no lado do servidor / servlet.

Isso acontece mesmo se você definir

crossOrigin: false;

ou mesmo se você omitir.

A razão é simplesmente essa localhost != localhost:57124. Tente enviar apenas para localhostsem a porta - irá falhar, porque o destino solicitado não será alcançável, porém observe que se os nomes de domínio forem iguais a solicitação é enviada sem a solicitação OPTIONS antes do POST.

Cair fora
fonte
3

Eu concordo com Kevin B, o relatório de bug diz tudo. Parece que você está tentando fazer chamadas Ajax entre domínios. Se você não estiver familiarizado com a política de mesma origem, pode começar aqui: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript .

Se não for uma chamada ajax de domínio cruzado, tente tornar seu URL de destino relativo e ver se o problema desaparece. Se você estiver realmente desesperado, dê uma olhada no JSONP, mas cuidado, o caos se esconde. Não há muito mais que possamos fazer para ajudá-lo.

jgitter
fonte
1
Nossa estrutura de sistema é algo que não posso mudar. Usar uma porta diferente é um requisito de nossa arquitetura. Recebo a mesma política de origem, mas achei que o CORS que implementamos era suficiente. Aparentemente não.
Corey Ogburn
2
Se o seu servidor retornar respostas JSON, você pode olhar para o método JSONP, apenas use-o com responsabilidade.
jgitter
1
Eu realmente não me importo em discutir com você, mas JSONP usa tags de script para extrair dados de outro domínio e, em seguida, envia o resultado para uma função de retorno de chamada. É muito mais difícil se o resultado não for JSON.
jgitter
1
Não, não é muito mais difícil. Na verdade, a resposta não deve ser um JSON válido em nenhum caso. Em vez disso, o servidor deve retornar algo como isto: callbackfunc(somedata). Como você pode ver, este não é um JSON válido. E somedatapode ser uma string, um número ou o que você quiser.
Ray Nicholus
1
Estou usando o Postman e aí os métodos de solicitação são enviados corretamente (por exemplo, 'PUT', 'DELETE', etc). Mas quando tento fazer a partir do meu código sempre os envio com o método de solicitação OPTIONS. Não tenho ideia de como Postman é capaz de fazer isso.
ErwinGO
1

Se for possível, passe os parâmetros por meio de GET / POST regular com um nome diferente e deixe o código do lado do servidor lidar com isso.

Tive um problema semelhante com meu próprio proxy para ignorar o CORS e recebi o mesmo erro de POST-> OPTION no Chrome. Era o Authorizationcabeçalho no meu caso ( "x-li-format"e "X-UserName"aqui no seu caso). Acabei passando em um formato fictício (por exemplo, AuthorizatinJackem GET) e mudei o código do meu proxy para transformá-lo em um cabeçalho ao fazer a chamada para o destino . Aqui está em PHP:

if (isset($_GET['AuthorizationJack'])) {
    $request_headers[] = "Authorization: Basic ".$_GET['AuthorizationJack'];
}
Auxílio em
fonte
1

No meu caso estou chamando uma API hospedada pela AWS (API Gateway). O erro aconteceu quando tentei chamar a API de um domínio diferente do próprio domínio da API. Como sou o proprietário da API, habilitei o CORS para o ambiente de teste, conforme descrito na documentação da Amazon .

Na produção este erro não acontecerá, pois a requisição e a api estarão no mesmo domínio.

Espero que ajude!

gbonesso
fonte
0

Como respondido por @Dark Falcon, simplesmente resolvi o problema .

No meu caso, estou usando o servidor node.js e criando uma sessão se ela não existir. Como o método OPTIONS não contém os detalhes da sessão, ele acaba criando uma nova sessão para cada solicitação do método POST.

Portanto, em minha rotina de aplicativo para criar sessão se não existir, acabei de adicionar uma verificação para ver se o método é OPTIONSe, se for, apenas pule a parte de criação da sessão:

    app.use(function(req, res, next) {
        if (req.method !== "OPTIONS") {
            if (req.session && req.session.id) {
                 // Session exists
                 next();
            }else{
                 // Create session
                 next();
          }
        } else {
           // If request method is OPTIONS, just skip this part and move to the next method.
           next(); 
        }
    }
Mahesh
fonte
0

As solicitações "preflighted" enviam primeiro uma solicitação HTTP pelo método OPTIONS para o recurso no outro domínio, a fim de determinar se a solicitação real é segura para envio. Solicitações entre sites

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Noorullah
fonte
1
Você poderia adicionar mais informações? Sua resposta parece um comentário. :)
Badacadabra
0

Considere o uso de axios

axios.get( url,
{ headers: {"Content-Type": "application/json"} } ).then( res => {

  if(res.data.error) {

  } else { 
    doAnything( res.data )
  }

}).catch(function (error) {
   doAnythingError(error)
});

Eu tive esse problema usando fetch e axios funcionou perfeitamente.

Evhz
fonte
5
Axios também usa as primeiras OPÇÕES
Skylin R
0

Eu encontrei um problema muito semelhante. Passei quase meio dia para entender por que tudo funciona corretamente no Firefox e falha no Chrome. No meu caso, foi por causa de campos duplicados (ou talvez digitados incorretamente) no cabeçalho da minha solicitação.

Andrew Tatomyr
fonte
0

Use fetch em vez de XHR, então a solicitação não será pré-iluminada, mesmo que seja de vários domínios.

Fei Sun
fonte
-1
 $.ajax({
            url: '###',
            contentType: 'text/plain; charset=utf-8',
            async: false,
            xhrFields: {
                withCredentials: true,
                crossDomain: true,
                Authorization: "Bearer ...."
            },

            method: 'POST',

            data: JSON.stringify( request ),
            success: function (data) {
                console.log(data);
            }
        });

o contentType: 'text / plain; charset = utf-8 ', ou apenas contentType:' text / plain ', funciona para mim! Saudações!!

David Lopes
fonte
O que isso tem a ver com a pergunta?
Corey Ogburn de
HI, acho que isso resolve o problema do título, com esse tipo de conteúdo você passa o método OPÇÕES. Atenciosamente
David Lopes
ContentType não tem nada a ver com o método.
Corey Ogburn
Eu sei o que você está dizendo, mas tente. dependendo do navegador, seu tipo de conteúdo pode influenciar sua solicitação e alterar seu método!
David Lopes