jQuery $ .ajax (), $ .post enviando "OPTIONS" como REQUEST_METHOD no Firefox


330

Tendo problemas com o que eu pensava ser um plugin jQuery relativamente simples ...

O plugin deve buscar dados de um script php via ajax para adicionar opções a <select>. A solicitação ajax é bastante genérica:

$.ajax({
  url: o.url,
  type: 'post',
  contentType: "application/x-www-form-urlencoded",
  data: '{"method":"getStates", "program":"EXPLORE"}',
  success: function (data, status) {
    console.log("Success!!");
    console.log(data);
    console.log(status);
  },
  error: function (xhr, desc, err) {
    console.log(xhr);
    console.log("Desc: " + desc + "\nErr:" + err);
  }
});

Isso parece funcionar bem no Safari. No Firefox 3.5, o REQUEST_TYPEservidor sempre é 'OPTIONS' e os dados $ _POST não aparecem. O Apache registra a solicitação como tipo 'OPÇÕES':

::1 - - [08/Jul/2009:11:43:27 -0500] "OPTIONS sitecodes.php HTTP/1.1" 200 46

Por que essa chamada ajax funcionaria no Safari, mas não no Firefox, e como faço para corrigi-la no Firefox?

Cabeçalhos de resposta
Data: Quarta, 08 de julho de 2009 21:22:17 GMT
Servidor: Apache / 2.0.59 (Unix) PHP / 5.2.6 DAV / 2
X-Powered-By: PHP / 5.2.6
Comprimento do conteúdo 46
Tempo limite do Keep-Alive = 15, max = 100
Conexão Keep-Alive
Tipo de conteúdo text / html

Cabeçalhos de solicitação
Formulário de pedido do host: 8888
Agente do usuário Mozilla / 5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv: 1.9.1) Gecko / 20090624 Firefox / 3.5
Aceitar texto / html, aplicativo / xhtml + xml, aplicativo / xml; q = 0,9, * / *; q = 0,8
Idioma de aceitação en-us, en; q = 0,5
Aceitar-codificação gzip, deflate
Aceitar conjunto de caracteres ISO-8859-1, utf-8; q = 0,7, *; q = 0,7
Keep-Alive 300
Conexão mantida ativa
Origem http://ux.inetu.act.org
Método de solicitação de controle de acesso POST
Cabeçalhos de solicitação de controle de acesso x-request-with

Aqui está uma imagem da saída do Firebug:


Você pode postar a resposta do firebug e solicitar cabeçalhos. Não estou recebendo nenhum erro quando executo código semelhante no Firefox.
MitMaro

Adicionadas informações de cabeçalho e uma imagem do Firebug.
Fitzgeraldsteele #

Só tive esse mesmo problema ao implementar um servidor da web incorporado. Obrigado por perguntar :)
Robert Gould

Se você está procurando um Java soluções JAX-RS, consulte aqui: Access-Control-Allow-Origin
Tobias Sarnow

O comportamento do firefox parece ter mudado agora? Não recebo nenhum pedido de opção.
Buge

Respostas:


169

O motivo do erro é a mesma política de origem. Ele só permite que você execute XMLHTTPRequests em seu próprio domínio. Veja se você pode usar um retorno de chamada JSONP :

$.getJSON( 'http://<url>/api.php?callback=?', function ( data ) { alert ( data ); } );

26
por que o firefox é o único navegador a fazer isso? Eu quero um post, não um get.
Maslow

11
Crossite-POST: Alguém conhece uma solução para fazer um POST com application / json como tipo de conteúdo?
precisa saber é o seguinte

13
Então, qual é exatamente a solução?
Nik So

3
Procurar uma solução para isso também e usar getJSON em vez de chamada ajax não faz isso por mim, pois é muito mais limitado.
Timo Wallenius

1
@schoetbi, para isso você precisará usar o CORS, que é bem suportado em navegadores mais recentes ... suporte limitado no IE8-9 e precisa de suporte do lado do servidor.
precisa

57

Eu usei o seguinte código no lado do Django para interpretar a solicitação OPTIONS e definir os cabeçalhos de controle de acesso necessários. Depois disso, meus pedidos de vários domínios do Firefox começaram a funcionar. Como dito anteriormente, o navegador primeiro envia a solicitação OPTIONS e, em seguida, imediatamente depois disso, o POST / GET

def send_data(request):
    if request.method == "OPTIONS": 
        response = HttpResponse()
        response['Access-Control-Allow-Origin'] = '*'
        response['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
        response['Access-Control-Max-Age'] = 1000
        # note that '*' is not valid for Access-Control-Allow-Headers
        response['Access-Control-Allow-Headers'] = 'origin, x-csrftoken, content-type, accept'
        return response
    if request.method == "POST":
        # ... 

Editar: parece que, pelo menos em alguns casos, você também precisa adicionar os mesmos cabeçalhos de controle de acesso à resposta real. Isso pode ser um pouco confuso, pois a solicitação parece ter êxito, mas o Firefox não transmite o conteúdo da resposta ao Javascript.


Sua edição sobre a resposta POST / GET real é um pouco assustadora; se alguém puder confirmar isso, informe-nos aqui!
Arjan

Não sei se é um bug ou um recurso, mas parece que alguém já percebeu isso também. Veja, por exemplo, kodemaniak.de/?p=62 e procure por "corpo de resposta vazio"
Juha Palomäki

2
Há uma diferença entre solicitações simples e aquelas que precisam de comprovação. Sua "solução" funcionará apenas com solicitações de comprovação, portanto não é uma solução real. Sempre que você receber um cabeçalho "Origem:" nos cabeçalhos da solicitação, responda com isso sendo permitido.
odinho - Velmont

1
Eu acredito que o cabeçalho Access-Control-Allow-Headersdeve conter o valor x-csrf-token, não x-csrftoken.
precisa

16

Este artigo do mozilla developer center descreve vários cenários de solicitação entre domínios. O artigo parece indicar que uma solicitação POST com o tipo de conteúdo 'application / x-www-form-urlencoded' deve ser enviada como uma 'solicitação simples' (sem a solicitação OPTIONS 'preflight'). Descobri, no entanto, que o Firefox enviou a solicitação OPTIONS, mesmo que meu POST tenha sido enviado com esse tipo de conteúdo.

Consegui fazer isso funcionar criando um manipulador de solicitação de opções no servidor, que definiu o cabeçalho de resposta 'Access-Control-Allow-Origin' como '*'. Você pode ser mais restritivo configurando-o para algo específico, como ' http://someurl.com '. Além disso, li que, supostamente, você pode especificar uma lista separada por vírgula de várias origens, mas não consegui fazer isso funcionar.

Depois que o Firefox recebe a resposta à solicitação OPTIONS com um valor aceitável 'Access-Control-Allow-Origin', ele envia a solicitação POST.


15

Corrigi esse problema usando uma solução totalmente baseada em Apache. No meu vhost / htaccess, coloquei o seguinte bloco:

# enable cross domain access control
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"

# force apache to return 200 without executing my scripts
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* / [R=200,L]

Você pode não precisar da última parte, dependendo do que acontecer quando o Apache executar o script de destino. O crédito vai para o pessoal amigável do ServerFault para a última parte.


Sua resposta me ajudou, mas se precisar de alguma lógica por trás do CORS, ela não resolve completamente.
Ratata Tata

10

Esse PHP na parte superior do script de resposta parece funcionar. (Com o Firefox 3.6.11. Ainda não fiz muitos testes.)

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Max-Age: 1000');
if(array_key_exists('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', $_SERVER)) {
    header('Access-Control-Allow-Headers: '
           . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
} else {
    header('Access-Control-Allow-Headers: *');
}

if("OPTIONS" == $_SERVER['REQUEST_METHOD']) {
    exit(0);
}

Isso pode ser uma questão de gosto, mas sempre enviando essas cabeçalhos de resposta (também para GET, POST...) é um pouco demais para o meu gosto. (E, eu me pergunto se sempre enviando essas cumpre com as especificações?)
Arjan

3
envolva-o em if ($ _ SERVER ['HTTP_ORIGIN']). Se esse cabeçalho estiver lá, é uma solicitação do CORS; caso contrário, não será necessário enviar nada.
odinho - Velmont

7

Eu tive o mesmo problema com o envio de solicitações para o Google Maps, e a solução é bastante simples com o jQuery 1.5 - para uso do dataType dataType: "jsonp"


12
Incompatível com o método POST.
Pavel Vlasov

1
Funciona com um método GET, mas é uma solução muito limitada. Por exemplo, ao fazer isso, você não pode enviar de volta uma resposta com um cabeçalho específico, incluindo um token.
21412 sv16r

6

O culpado é a solicitação de comprovação usando o método OPTIONS

Para métodos de solicitação HTTP que podem causar efeitos colaterais nos dados do usuário (em particular, para métodos HTTP diferentes de GET ou para uso POST com certos tipos MIME), a especificação exige que os navegadores "preflight" a solicitação, solicitando métodos suportados do servidor com um método de solicitação HTTP OPTIONS e, mediante "aprovação" do servidor, enviando a solicitação real com o método de solicitação HTTP real.

Especificação da Web, consulte: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Resolvi o problema adicionando as seguintes linhas no Nginx conf.

    location / {
               if ($request_method = OPTIONS ) {
                   add_header Access-Control-Allow-Origin  "*";
                   add_header Access-Control-Allow-Methods "POST, GET, PUT, UPDATE, DELETE, OPTIONS";
                   add_header Access-Control-Allow-Headers "Authorization";
                   add_header Access-Control-Allow-Credentials  "true";
                   add_header Content-Length 0;
                   add_header Content-Type text/plain;
                   return 200;
               }
    location ~ ^/(xxxx)$ {
                if ($request_method = OPTIONS) {
                    rewrite ^(.*)$ / last;
                }
    }

1
Esta resposta é muito útil obrigado. O fato de o navegador estar enviando uma solicitação de comprovação com um método OPTIONS não é óbvio.
Normangorman

4

Eu estava olhando através da fonte 1.3.2, ao usar JSONP, a solicitação é feita criando um elemento SCRIPT dinamicamente, que ultrapassa a política de mesmo domínio dos navegadores. Naturalmente, você não pode fazer uma solicitação POST usando um elemento SCRIPT, o navegador buscará o resultado usando GET.

Como você está solicitando uma chamada JSONP, o elemento SCRIPT não é gerado, porque só faz isso quando a chamada Type of AJAX está configurada como GET.

http://dev.jquery.com/ticket/4690


4

Tivemos um problema como este com o ASP.Net. Nosso IIS estava retornando um erro interno do servidor ao tentar executar um jQuery $.postpara obter algum conteúdo html devido ao PageHandlerFactory.GET,HEAD,POST,DEBUG verbos. Para que você possa alterar essa restrição, adicione o verbo "OPTIONS" à lista ou selecione "All Verbs"

Você pode modificar isso no Gerenciador do IIS, selecionando seu site e, em seguida, selecionando Mapeamentos de manipulador, clique duas vezes em seu PageHandlerFactory para arquivos * .apx conforme necessário (usamos o pool de aplicativos integrado com a estrutura 4.0). Clique em Solicitar restrições, vá para a guia Verbos e aplique sua modificação.

Agora nosso $.postpedido está funcionando como esperado :)


2

Verifique se o actionURL do seu formulário inclui a wwwparte do domínio, enquanto a página original que você abriu é exibida sem www.

Normalmente feito para URLs canônicos.

Eu lutei por horas antes de me deparar com este artigo e encontrei a dica do Cross Domain.


2

Parece que, se o.url = 'index.php'esse arquivo existir, está ok e retornando uma mensagem de sucesso no console. Retorna um erro se eu usar url:http://www.google.com

Ao fazer uma solicitação de postagem, por que não usar diretamente o método $ .post :

$.post("test.php", { func: "getNameAndTime" },
    function(data){
        alert(data.name); // John
        console.log(data.time); //  2pm
    }, "json");

É muito mais simples.


Tem a mesma coisa com isso ... pensei que eu deveria usar $ .ajax () para que eu pudesse, pelo menos, obter algumas informações de depuração na condição de erro ..
fitzgeraldsteele


1

A solução para isso é:

  1. use dataType: json
  2. adicione &callback=?ao seu url

isso funcionou ao chamar a API do Facebook e com o Firefox. O Firebug está usando em GETvez das OPTIONScondições acima (ambas).




0

Tente adicionar a opção:

dataType: "json"


2
que funcionou, por que o json é considerado "seguro" para solicitações entre domínios?
Nik Então,

0
 function test_success(page,name,id,divname,str)
{ 
 var dropdownIndex = document.getElementById(name).selectedIndex;
 var dropdownValue = document.getElementById(name)[dropdownIndex].value;
 var params='&'+id+'='+dropdownValue+'&'+str;
 //makerequest_sp(url, params, divid1);

 $.ajax({
    url: page,
    type: "post",
    data: params,
    // callback handler that will be called on success
    success: function(response, textStatus, jqXHR){
        // log a message to the console
        document.getElementById(divname).innerHTML = response;

        var retname = 'n_district';
        var dropdownIndex = document.getElementById(retname).selectedIndex;
        var dropdownValue = document.getElementById(retname)[dropdownIndex].value;
        if(dropdownValue >0)
        {
            //alert(dropdownValue);
            document.getElementById('inputname').value = dropdownValue;
        }
        else
        {
            document.getElementById('inputname').value = "00";
        }
        return;
        url2=page2; 
        var params2 = parrams2+'&';
        makerequest_sp(url2, params2, divid2);

     }
});         
}

A pergunta já foi respondida há 6 meses. Como isso resolve isso?
Barmar

0

Eu tive um problema semelhante ao tentar usar a API do Facebook.

O único contentType que não enviou a solicitação Comprovada parecia ser apenas texto / simples ... não o restante dos parâmetros mencionados no mozilla aqui

  • Por que esse é o único navegador que faz isso?
  • Por que o Facebook não conhece e aceita a solicitação de comprovação?

FYI: O documento Moz mencionado acima sugere que os cabeçalhos X-Lori devem acionar uma solicitação Comprovada ... isso não acontece.


0

Você precisa fazer algum trabalho no lado do servidor. Vejo que você está usando PHP no lado do servidor, mas a solução para o aplicativo Web .NET está aqui: Não é possível definir o tipo de conteúdo como 'application / json' em jQuery.ajax

Faça o mesmo no script PHP e ele funcionará. Simplesmente: Na primeira solicitação, o navegador está perguntando ao servidor se é permitido enviar esses dados com esse tipo e a segunda solicitação é adequada / permitida.


0

Tente adicionar o seguinte:

dataType: "json",
ContentType: "application/json",
data: JSON.stringify({"method":"getStates", "program":"EXPLORE"}),  

0

Usei um URL de proxy para resolver um problema semelhante quando quero postar dados no meu apache solr hospedado em outro servidor. (Essa pode não ser a resposta perfeita, mas resolve o meu problema.)

Siga este URL: Usando o modo Rewrite para proxying , adiciono esta linha ao meu httpd.conf:

 RewriteRule ^solr/(.*)$ http://ip:8983/solr$1 [P]

Portanto, eu posso apenas postar dados em / solr em vez de postar dados em http: // ip: 8983 / solr / *. Em seguida, ele postará dados na mesma origem.


0

Eu já tenho esse código lidando bem com minha situação de cors no php:

header( 'Access-Control-Allow-Origin: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Headers: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Credentials: true' );

E estava funcionando bem local e remotamente, mas não para envios quando remotos.

Algo aconteceu com o apache / php OU meu código, não me preocupei em procurá-lo, quando você solicita OPÇÕES, ele retorna meu cabeçalho com regras cors, mas com resultado 302. Portanto, meu navegador não reconhece como uma situação aceitável.

O que fiz, com base na resposta do @Mark McDonald, é apenas colocar esse código após o meu cabeçalho:

if( $_SERVER['REQUEST_METHOD'] === 'OPTIONS' )
{
    header("HTTP/1.1 202 Accepted");
    exit;
}

Agora, ao solicitar OPTIONS, basta enviar o cabeçalho e o resultado 202.


-1

Por favor, esteja ciente de:

JSONP suporta apenas o método de solicitação GET.

* Enviar solicitação pelo firefox : *

$.ajax({
   type: 'POST',//<<===
   contentType: 'application/json',
   url: url,
   dataType: "json"//<<=============
    ...
});

Acima da solicitação, envie por OPTIONS (while ==> digite: 'POST' ) !!!!

$.ajax({
    type: 'POST',//<<===
    contentType: 'application/json',
    url: url,
    dataType: "jsonp"//<<==============
    ...
});

Mas acima da solicitação enviada por GET (while ==> digite: 'POST' ) !!!!

Quando você estiver em "comunicação entre domínios", preste atenção e tenha cuidado.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.