jQuery $ .ajax (), $ .post che invia "OPTIONS" come REQUEST_METHOD in Firefox


330

Problemi con quello che pensavo fosse un plugin jQuery relativamente semplice ...

Il plugin dovrebbe recuperare i dati da uno script php tramite Ajax per aggiungere opzioni a <select>. La richiesta Ajax è piuttosto generica:

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

Questo sembra funzionare bene in Safari. In Firefox 3.5, REQUEST_TYPEsul server è sempre 'OPTIONS' e i dati $ _POST non vengono visualizzati. Apache registra la richiesta come tipo "OPZIONI":

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

Perché questa chiamata Ajax dovrebbe funzionare in Safari, ma non in Firefox, e come posso ripararla per Firefox?

Header di risposta
Data: mer 08 lug 2009 21:22:17 GMT
Server: Apache / 2.0.59 (Unix) PHP / 5.2.6 DAV / 2
X-Powered-By: PHP / 5.2.6
Lunghezza contenuto 46
Timeout keep-alive = 15, max = 100
Connessione Keep-Alive
Tipo di contenuto text / html

Richiedi intestazioni
Modulo d'ordine dell'host: 8888
User-Agent Mozilla / 5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv: 1.9.1) Gecko / 20090624 Firefox / 3.5
Accetta testo / html, application / xhtml + xml, application / xml; q = 0.9, * / *; q = 0.8
Accept-Language en-us, en; q = 0,5
Accetta-codifica gzip, deflate
Accept-Charset ISO-8859-1, utf-8; q = 0.7, *; q = 0.7
Keep-Alive 300
Connessione keep-alive
Origine http://ux.inetu.act.org
POST Metodo di controllo accessi
Access-Control-Request-Headers x-richiesto-con

Ecco un'immagine dell'output di Firebug:


Puoi pubblicare la risposta di firebug e richiedere le intestazioni. Non visualizzo alcun errore quando eseguo un codice simile in Firefox.
MitMaro,

Aggiunte informazioni sull'intestazione e un'immagine da Firebug.
fitzgeraldsteele,

Ho appena avuto lo stesso problema durante l'implementazione di un server web incorporato. Grazie per averlo chiesto :)
Robert Gould,

Se stai cercando una soluzione Java JAX-RS, vedi qui: Access-Control-Allow-Origin
Tobias Sarnow

Il comportamento di Firefox sembra essere cambiato ora? Non ricevo alcuna richiesta di opzione.
Buge,

Risposte:


169

Il motivo dell'errore è la stessa politica di origine. Ti consente solo di fare richieste XMLHTTP al tuo dominio. Vedi invece se puoi usare un callback JSONP :

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

26
perché Firefox è l'unico browser a farlo? Voglio un post non un get.
Maslow

11
Crossite-POST: qualcuno conosce una soluzione per eseguire un POST con application / json come Content-Type?
schoetbi,

13
Allora, qual è esattamente la soluzione?
Nik So,

3
Cercare una soluzione anche a questo e usare getJSON invece di ajax call non lo fa per me in quanto è molto più limitato.
Timo Wallenius,

1
@schoetbi per questo avrai bisogno di usare CORS, che è ben supportato nei browser più recenti ... supporto limitato in IE8-9 e ha bisogno del supporto lato server.
Tracker1

57

Ho usato il seguente codice sul lato Django per interpretare la richiesta OPTIONS e impostare le intestazioni Access-Control richieste. Dopo questo le mie richieste interdominio da Firefox hanno iniziato a funzionare. Come detto prima, il browser invia prima la richiesta OPTIONS e poi immediatamente dopo il 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":
        # ... 

Modifica: sembra che almeno in alcuni casi sia necessario aggiungere le stesse intestazioni Access-Control alla risposta effettiva. Questo può essere un po 'confuso, poiché la richiesta sembra avere successo, ma Firefox non passa il contenuto della risposta a Javascript.


La tua modifica sull'effettiva risposta POST / GET è un po 'spaventosa; se qualcuno può confermarlo, faccelo sapere qui!
Arjan,

Non so se si tratti di un bug o di una funzionalità, ma sembra che anche qualcun altro l'abbia notato. Vedi ad esempio kodemaniak.de/?p=62 e cerca "corpo di risposta vuoto"
Juha Palomäki,

2
C'è una differenza tra le richieste semplici e quelle che richiedono il preflight. La tua "soluzione" funzionerà solo con richieste di verifica preliminare, quindi non è una soluzione reale. Ogni volta che ottieni un'intestazione "Origine:" nelle intestazioni della richiesta, dovresti rispondere con quello autorizzato.
odinho - Velmont,

1
Credo che l'intestazione Access-Control-Allow-Headersdovrebbe contenere il valore x-csrf-token, no x-csrftoken.
JellicleCat

16

Questo articolo del centro per sviluppatori di Mozilla descrive vari scenari di richiesta tra domini. L'articolo sembra indicare che una richiesta POST con tipo di contenuto di "application / x-www-form-urlencoded" dovrebbe essere inviata come "semplice richiesta" (senza richiesta "preflight" OPTIONS). Ho scoperto, tuttavia, che Firefox ha inviato la richiesta OPTIONS, anche se il mio POST è stato inviato con quel tipo di contenuto.

Sono stato in grado di farlo funzionare creando un gestore di richieste di opzioni sul server, che ha impostato l'intestazione di risposta 'Access-Control-Allow-Origin' su '*'. Puoi essere più restrittivo impostandolo su qualcosa di specifico, come " http://someurl.com ". Inoltre, ho letto che, presumibilmente, puoi specificare un elenco separato da virgole di più origini, ma non sono riuscito a farlo funzionare.

Quando Firefox riceve la risposta alla richiesta OPTIONS con un valore 'Access-Control-Allow-Origin' accettabile, invia la richiesta POST.


15

Ho risolto questo problema usando una soluzione interamente basata su Apache. Nel mio vhost / htaccess ho inserito il seguente blocco:

# 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]

Potrebbe non essere necessaria l'ultima parte, a seconda di cosa succede quando Apache esegue lo script di destinazione. Il merito va all'amichevole ServerFault per l'ultima parte.


La tua risposta mi ha aiutato, ma se è necessaria una logica dietro CORS, non si risolve completamente.
Ratata Tata,

10

Questo PHP nella parte superiore dello script di risposta sembra funzionare. (Con Firefox 3.6.11. Non ho ancora fatto molti test.)

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

Questo potrebbe essere una questione di gusti, ma sempre l'invio di queste intestazioni di risposta (anche per GET, POST, ...) è un po 'troppo per i miei gusti. (E, mi chiedo se invii sempre quelli conformi alle specifiche?)
Arjan,

3
racchiudilo in if ($ _ SERVER ['HTTP_ORIGIN']). Se quell'intestazione è lì, è una richiesta CORS, in caso contrario, non è necessario inviare nulla.
Odinho - Velmont,

7

Ho avuto lo stesso problema con l'invio di richieste a Google Maps e la soluzione è abbastanza semplice con jQuery 1.5 - per l'utilizzo di dataType dataType: "jsonp"


12
Incompatibile con il metodo POST.
Pavel Vlasov,

1
Funziona con un metodo GET ma è una soluzione molto limitata. Ad esempio, facendo ciò non è possibile inviare una risposta con un'intestazione specifica incluso un token.
svassr,

6

Culprit è una richiesta di verifica preliminare che utilizza il metodo OPTIONS

Per i metodi di richiesta HTTP che possono causare effetti collaterali sui dati utente (in particolare, per metodi HTTP diversi da GET o per l'utilizzo POST con determinati tipi MIME), la specifica impone che i browser "eseguano il preflight" della richiesta, sollecitando i metodi supportati dal server con un metodo di richiesta OPTIONS HTTP, quindi, dopo "approvazione" dal server, inviando la richiesta effettiva con il metodo di richiesta HTTP effettivo.

Le specifiche Web si riferiscono a: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Ho risolto il problema aggiungendo le seguenti righe in 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
Questa risposta è molto utile, grazie. Il fatto che il browser stia inviando una richiesta di verifica preliminare con un metodo OPTIONS non è ovvio.
Normangorman,

4

Stavo guardando attraverso la fonte 1.3.2, quando usavo JSONP, la richiesta viene fatta costruendo un elemento SCRIPT in modo dinamico, che supera i criteri dello stesso dominio del browser. Naturalmente, non è possibile effettuare una richiesta POST utilizzando un elemento SCRIPT, il browser recupera il risultato utilizzando GET.

Quando si richiede una chiamata JSONP, l'elemento SCRIPT non viene generato, poiché lo fa solo quando il Tipo di chiamata AJAX è impostato su GET.

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


4

Abbiamo avuto un problema come questo con ASP.Net. Il nostro IIS stava restituendo un errore interno del server durante il tentativo di eseguire un jQuery $.postper ottenere alcuni contenuti html a causa di PageHandlerFactory era limitato a rispondere soloGET,HEAD,POST,DEBUG verbi. Quindi puoi modificare tale restrizione aggiungendo il verbo "OPZIONI" all'elenco o selezionando "Tutti i verbi"

Puoi modificarlo in Gestione IIS, selezionando il tuo sito Web, quindi selezionando Mapping gestori, fai doppio clic nel tuo PageHandlerFactory per i file * .apx di cui hai bisogno (Usiamo il pool di applicazioni integrate con Framework 4.0). Fai clic su Richiedi restrizioni, quindi vai a Verbi Tabn e applica la modifica.

Ora la nostra $.postrichiesta funziona come previsto :)


2

Verifica se l' actionURL del modulo include la wwwparte del dominio, mentre la pagina originale che hai aperto viene visualizzata senza www.

Tipicamente fatto per URL canonici.

Ho lottato per ore prima di imbattermi in questo articolo e ho trovato il suggerimento di Cross Domain.


2

Sembra che se o.url = 'index.php'e questo file esista sia ok e restituisca un messaggio di successo nella console. Restituisce un errore se uso url:http://www.google.com

Se stai facendo una richiesta di posta, perché non usare direttamente il metodo $ .post :

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

È molto più semplice.


Ho ottenuto la stessa cosa con questo ... ho pensato di usare $ .ajax () in modo da poter ottenere almeno alcune informazioni di debug sulla condizione di errore ..
fitzgeraldsteele


1

La soluzione a questo è:

  1. usa dataType: json
  2. aggiungi &callback=?al tuo URL

questo ha funzionato chiamando l'API di Facebook e con Firefox. Firebug utilizza GETinvece che OPTIONScon le condizioni sopra (entrambe).


1

Un'altra possibilità per aggirare il problema è utilizzare uno script proxy. Tale metodo è descritto ad esempio qui



0

Prova ad aggiungere l'opzione:

dataType: "json"


2
che ha funzionato, perché json è considerato "sicuro" per le richieste tra domini?
Nik So,

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

     }
});         
}

Alla domanda è già stata data risposta 6 mesi fa. Come lo risolve?
Barmar

0

Ho avuto un problema simile con il tentativo di utilizzare l'API di Facebook.

L'unico contentType che non ha inviato la richiesta di preflight sembrava essere solo text / plain ... non il resto dei parametri menzionati su mozilla qui

  • Perché questo è l'unico browser che lo fa?
  • Perché Facebook non conosce e accetta la richiesta di verifica preliminare?

Cordiali saluti: Il suddetto documento Moz suggerisce che le intestazioni di X-Lori dovrebbero innescare una richiesta preflight ... non lo fa.



0

Prova ad aggiungere quanto segue:

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

0

Ho usato un URL proxy per risolvere un problema simile quando desidero pubblicare dati sul mio apache solr ospitato su un altro server. (Questa potrebbe non essere la risposta perfetta ma risolve il mio problema.)

Segui questo URL: Usando Mode-Rewrite per il proxy , aggiungo questa riga al mio httpd.conf:

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

Pertanto, posso semplicemente pubblicare dati su / solr invece di pubblicare dati su http: // ip: 8983 / solr / *. Quindi pubblicherà i dati nella stessa origine.


0

Ho già questo codice che gestisce bene la mia situazione cors in php:

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

E funzionava bene localmente e in remoto, ma non per i caricamenti in remoto.

Qualcosa accade con apache / php O il mio codice, non mi sono preoccupato di cercarlo, quando richiedi OPZIONI restituisce la mia intestazione con le regole cors ma con 302 risultati. Pertanto il mio browser non riconosce una situazione accettabile.

Quello che ho fatto, basato sulla risposta di @Mark McDonald, è stato semplicemente inserire questo codice dopo la mia intestazione:

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

Ora, quando richiesto OPTIONS, invierà solo l'intestazione e il risultato 202.


-1

Si prega di essere avvisato:

JSONP supporta solo il metodo di richiesta GET.

* Invia richiesta da firefox : *

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

Sopra la richiesta invia tramite OPTIONS (while ==> type: 'POST' ) !!!!

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

Ma sopra la richiesta invia da GET (mentre ==> digita: 'POST' ) !!!!

Quando sei in "comunicazione tra domini", presta attenzione e stai attento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.