Uma solicitação CORS POST funciona com JavaScript simples, mas por que não com jQuery?

88

Estou tentando fazer uma solicitação de postagem Cross Origin, e consegui funcionar da JavaScriptseguinte maneira:

var request = new XMLHttpRequest();
var params = "action=something";
request.open('POST', url, true);
request.onreadystatechange = function() {if (request.readyState==4) alert("It worked!");};
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.setRequestHeader("Content-length", params.length);
request.setRequestHeader("Connection", "close");
request.send(params);

Mas eu gostaria de usar jQuery, mas não consigo fazer funcionar. Isso é o que estou tentando:

$.ajax(url, {
    type:"POST",
    dataType:"json",
    data:{action:"something"}, 
    success:function(data, textStatus, jqXHR) {alert("success");},
    error: function(jqXHR, textStatus, errorThrown) {alert("failure");}
});

Isso resulta em falha. Se alguém souber por jQueryque não funciona, entre em contato conosco. Obrigado.

(Estou usando jQuery1.5.1 e Firefox 4.0, e meu servidor está respondendo com um Access-Control-Allow-Origincabeçalho adequado )

Magmático
fonte
Esta foi a solução para mim (use XMLHttpRequest do Javascript) ao enfrentar problemas de CORS com o framework Ionic 3.
jeudyx

Respostas:

73

ATUALIZAÇÃO: como TimK apontou, isso não é mais necessário com jquery 1.5.2. Mas se você deseja adicionar cabeçalhos personalizados ou permitir o uso de credenciais (nome de usuário, senha ou cookies, etc.), continue lendo.


Acho que encontrei a resposta! (4 horas e muitos palavrões depois)

//This does not work!!
Access-Control-Allow-Headers: *

Você precisa especificar manualmente todos os cabeçalhos que deseja aceitar (pelo menos esse era o meu caso no FF 4.0 e no Chrome 10.0.648.204).

O método $ .ajax do jQuery envia o cabeçalho "x-required-with" para todas as solicitações de domínio cruzado (acho que é apenas domínio cruzado).

Portanto, o cabeçalho ausente necessário para responder à solicitação OPTIONS é:

//no longer needed as of jquery 1.5.2
Access-Control-Allow-Headers: x-requested-with

Se você estiver passando quaisquer cabeçalhos não "simples", precisará incluí-los em sua lista (envio mais um):

//only need part of this for my custom header
Access-Control-Allow-Headers: x-requested-with, x-requested-by

Então, para colocar tudo junto, aqui está meu PHP:

// * wont work in FF w/ Allow-Credentials
//if you dont need Allow-Credentials, * seems to work
header('Access-Control-Allow-Origin: http://www.example.com');
//if you need cookies or login etc
header('Access-Control-Allow-Credentials: true');
if ($this->getRequestMethod() == 'OPTIONS')
{
  header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
  header('Access-Control-Max-Age: 604800');
  //if you need special headers
  header('Access-Control-Allow-Headers: x-requested-with');
  exit(0);
}
Will Mason
fonte
5
Observe que o jQuery 1.5.2 mudou seu comportamento. Ele não adiciona mais um cabeçalho "X-Requested-With", então isso pode não ser mais um problema. blog.jquery.com/2011/03/31/jquery-152-released (Bug 8423)
Magmatic
1
@TimK, você está certo! Eu não percebi que eles lançaram 1.5.2. Dito isso, isso também funciona se você precisar fazer um pré-voo. Eu atualizei minha resposta.
Will Mason,
Então, estou confuso. Você acabou tendo que escrever um script PHP intermediário de qualquer maneira? Então você não precisa se preocupar em usar Ajax, certo? Ou eu estou esquecendo de alguma coisa. Não existe uma solução somente para JavaScript?
Elisabeth
1
@Elisabeth Este método só funciona se você controlar o destino solicitado ... NÃO é um script intermediário. É o topo do nosso PHP da localização solicitada. Isso faz mais sentido?
Will Mason
2
Sim! obrigado Will. Achei que você pudesse controlar tudo do lado do cliente, mas parece que você precisa controlar as duas extremidades.
Elisabeth
18

Outra possibilidade é que a configuração dataType: jsonfaça com que o JQuery envie o Content-Type: application/jsoncabeçalho. Isso é considerado um cabeçalho não padrão pelo CORS e requer uma solicitação de comprovação do CORS. Então, algumas coisas para tentar:

1) Tente configurar seu servidor para enviar as respostas de comprovação adequadas. Isso estará na forma de cabeçalhos adicionais como Access-Control-Allow-Methodse Access-Control-Allow-Headers.

2) Abandone o dataType: json configuração. JQuery deve solicitar Content-Type: application/x-www-form-urlencodedpor padrão, mas apenas para ter certeza, você pode substituir dataType: jsonporcontentType: 'application/x-www-form-urlencoded'

monsur
fonte
Obrigado pelas ideias. Tentei não definir dataType e defini-lo como application/x-www-form-urlencodede uniforme text/plain. E tentei adicionar um cabeçalho de resposta de Access-Control-Allow-Methods "POST, GET, OPTIONS"Nada funcionou.
Magmatic
Você pode olhar no console de erro JavaScript (ou console do Firebug) e ver se há algum erro durante a solicitação? Além disso, se você sabe como usar o Wireshark, pode usá-lo para ver as solicitações HTTP reais que passam pela rede.
Monsur
1
"Outra possibilidade é que a configuração de dataType: json faça com que o JQuery envie o Content-Type: application / json header" - Isso não acontece. dataTypeinfluencia o Acceptcabeçalho da solicitação, mas não o Content-Typecabeçalho da solicitação.
Quentin
9

Você está enviando "params" em js: request.send(params);

mas "dados" em jquery ". Os dados estão definidos ?: data:data,

Além disso, você tem um erro no URL:

$.ajax( {url:url,
         type:"POST",
         dataType:"json",
         data:data, 
         success:function(data, textStatus, jqXHR) {alert("success");},
         error: function(jqXHR, textStatus, errorThrown) {alert("failure");}
});

Você está misturando a sintaxe com a de $ .post


Atualização : eu estava pesquisando com base na resposta de Monsur e descobri que você precisa adicionar Access-Control-Allow-Headers: Content-Type(abaixo está o parágrafo completo)

http://metajack.im/2010/01/19/crossdomain-ajax-for-xmpp-http-binding-made-easy/

Como funciona o CORS

CORS funciona de forma muito semelhante ao arquivo crossdomain.xml do Flash. Basicamente, o navegador enviará uma solicitação de domínio cruzado a um serviço, configurando a Origem do cabeçalho HTTP para o servidor solicitante. O serviço inclui alguns cabeçalhos como Access-Control-Allow-Origin para indicar se tal solicitação é permitida.

Para os gerenciadores de conexão BOSH, é suficiente especificar que todas as origens são permitidas, definindo o valor de Access-Control-Allow-Origin para *. O cabeçalho Content-Type também deve estar na lista branca no cabeçalho Access-Control-Allow-Headers.

Finalmente, para certos tipos de solicitações, incluindo solicitações do gerenciador de conexões BOSH, a verificação de permissões será pré-executada. O navegador fará uma solicitação OPTIONS e espera obter alguns cabeçalhos HTTP que indicam quais origens são permitidas, quais métodos são permitidos e quanto tempo essa autorização irá durar. Por exemplo, aqui está o que os patches Punjab e ejabberd que eu retornei para OPÇÕES:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type 
Access-Control-Max-Age: 86400
Aleadam
fonte
1
Desculpe. Sim. var data = {action:"something"}
Magmatic,
Você pode comparar a sintaxe de ambas as funções aqui: api.jquery.com/jQuery.post
Aleadam
Só tentei com a url nas configurações, mas mesmo problema. A função .ajax pode funcionar de qualquer maneira.
Magmatic,
Eu já tinha dois desses cabeçalhos. Eu adicionei os outros dois. Ainda "falha" com jQuery. O javascript simples ainda funciona.
Magmatic,
A última coisa que posso pensar é em usar api.jquery.com/jQuery.ajaxSetup para definir jQuery.ajaxSetup({'beforeSend': function(xhr) {xhr.setRequestHeader(string, string)}})e brincar com os diferentes cabeçalhos enviados (um exemplo para rails aqui: railscasts.com/episodes/136-jquery )
Aleadam
1

Cors muda o método de solicitação antes de terminar, de POST para OPTIONS, portanto, seus dados de postagem não serão enviados. A maneira que funcionou para lidar com esse problema de cors é executando a solicitação com ajax, que não suporta o método OPTIONS. código de exemplo:

        $.ajax({
            type: "POST",
            crossdomain: true,
            url: "http://localhost:1415/anything",
            dataType: "json",
            data: JSON.stringify({
                anydata1: "any1",
                anydata2: "any2",
            }),
            success: function (result) {
                console.log(result)
            },
            error: function (xhr, status, err) {
                console.error(xhr, status, err);
            }
        });

com estes cabeçalhos no servidor c #:

                    if (request.HttpMethod == "OPTIONS")
                    {
                          response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
                          response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
                          response.AddHeader("Access-Control-Max-Age", "1728000");
                    }
                    response.AppendHeader("Access-Control-Allow-Origin", "*");
Lucas silva de souza sandim
fonte
-2

Modifique seu Jquery da seguinte maneira:

$.ajax({
            url: someurl,
            contentType: 'application/json',
            data: JSONObject,
            headers: { 'Access-Control-Allow-Origin': '*' }, //add this line
            dataType: 'json',
            type: 'POST',                
            success: function (Data) {....}
});
Soma Sarkar
fonte
Por que eu iria querer fazer meu Ajax callas síncrono !?
Radko Dinev
contentType: 'application/json', data: JSONObject,- O servidor não está esperando JSON, portanto, enviar JSON não faria sentido. Além disso, não existe um objeto JSON .
Quentin
1
headers: { 'Access-Control-Allow-Origin': '*' }, //add this line- Nunca faça isso. Access-Control-Allow-Originé um cabeçalho de resposta , não um cabeçalho de solicitação. Na melhor das hipóteses, isso não fará nada. Na pior das hipóteses, ele converterá a solicitação de uma solicitação simples em uma solicitação preflight, o que torna cada vez mais difícil lidar com ela no servidor.
Quentin