AJAX in Chrome invia OPZIONI invece di GET / POST / PUT / DELETE?


107

Sto lavorando a un'applicazione web interna al lavoro. In IE10 le richieste funzionano bene, ma in Chrome tutte le richieste AJAX (che ce ne sono molte) vengono inviate utilizzando OPZIONI invece di qualunque metodo definito io fornisca. Tecnicamente le mie richieste sono "interdominio". Il sito è servito su localhost: 6120 e il servizio a cui sto inviando richieste AJAX è su 57124. Questo bug jquery chiuso definisce il problema, ma non una vera soluzione.

Cosa posso fare per utilizzare il metodo http corretto nelle richieste ajax?

Modificare:

Questo è nel caricamento del documento di ogni pagina:

jQuery.support.cors = true;

E ogni AJAX è costruito in modo simile:

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

2
L'ultimo commento in quel bug report lo spiega abbastanza bene ...
Kevin B

1
Mi è capitato di pensare perché tutto ciò che sto facendo è così vanigliato (e il mio codice è simile a quello del bug jquery). A parte questo, non è una scusa per non includerlo. BRB, prendendo del codice di esempio.
Corey Ogburn

3
Si noti che IE non prende in considerazione i numeri di porta quando determina se una richiesta è cross-origin.
Ray Nicholus

@KevinB: Il nostro servizio REST si avvale di richieste diverse poiché esegue operazioni diverse in base al metodo http. Passare tutto a GET non è una soluzione valida. Inoltre, secondo la risposta di Dark Falcon, non aiuterà comunque perché ho X-UserName e altre intestazioni personalizzate nelle richieste.
Corey Ogburn

ciò non cambia il fatto che se si desidera effettuare una richiesta tra origini, è necessario seguire tutte le regole applicabili alle richieste tra origini affinché funzioni correttamente. le richieste cross-origin in genere comportano una richiesta OPTIONS. Gestiscilo correttamente e il problema scomparirà. L'unico altro modo per risolvere questo problema (senza modificare l'API) è avere uno script sullo stesso server della pagina principale che interagisce con l'API utilizzando il codice lato server.
Kevin B

Risposte:


136

Chrome sta eseguendo il preflight della richiesta per cercare le intestazioni CORS . Se la richiesta è accettabile, invierà la richiesta reale. Se stai facendo questo cross-domain, dovrai semplicemente occupartene o trovare un modo per rendere la richiesta non cross-domain. Questo è il motivo per cui il bug di jQuery è stato chiuso come non risolto. Questo è di progettazione.

A differenza delle richieste semplici (discusse sopra), le richieste "preflight" inviano prima una richiesta HTTP mediante il metodo OPTIONS alla risorsa sull'altro dominio, al fine di determinare se la richiesta effettiva è sicura da inviare. Le richieste cross-site vengono preflight in questo modo poiché potrebbero avere implicazioni sui dati dell'utente. In particolare, viene eseguito il preflight di una richiesta se:

  • Utilizza metodi diversi da GET, HEAD o POST. Inoltre, se POST viene utilizzato per inviare dati di richiesta con un Content-Type diverso da application / x-www-form-urlencoded, multipart / form-data o text / plain, ad esempio se la richiesta POST invia un payload XML al server utilizzando application / xml o text / xml, la richiesta viene sottoposta a preflight.
  • Imposta intestazioni personalizzate nella richiesta (ad es. La richiesta utilizza un'intestazione come X-PINGOTHER)

20
Intestazioni personalizzate. Questo è probabilmente ciò che sta provocando le chiamate OPZIONI di preflight.
Corey Ogburn

18

Sulla base del fatto che la richiesta non viene inviata sulla porta predefinita 80/443, questa chiamata Ajax viene automaticamente considerata una richiesta di risorse cross-origin (CORS) , il che in altre parole significa che la richiesta emette automaticamente una richiesta OPTIONS che controlla Intestazioni CORS sul lato server / servlet.

Questo accade anche se imposti

crossOrigin: false;

o anche se lo ometti.

Il motivo è semplicemente quello localhost != localhost:57124. Prova a inviarlo solo a localhostsenza la porta - fallirà, perché il target richiesto non sarà raggiungibile, tuttavia nota che se i nomi di dominio sono uguali la richiesta viene inviata senza la richiesta OPTIONS prima del POST.


3

Sono d'accordo con Kevin B, il bug report dice tutto. Sembra che tu stia tentando di effettuare chiamate ajax interdominio. Se non hai familiarità con la stessa politica di origine, puoi iniziare da qui: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript .

Se questa non deve essere una chiamata ajax interdominio, prova a rendere relativo l'URL di destinazione e verifica se il problema scompare. Se sei davvero disperato, guarda nel JSONP, ma attenzione, il caos è in agguato. Non c'è davvero molto altro che possiamo fare per aiutarti.


1
La struttura del nostro sistema è qualcosa che non posso cambiare. L'utilizzo di una porta diversa è un requisito della nostra architettura. Ottengo la stessa politica di origine ma ho pensato che il CORS che abbiamo implementato fosse sufficiente. Apparentemente no.
Corey Ogburn

2
Se il tuo server restituisce risposte JSON, puoi esaminare il metodo JSONP, usalo in modo responsabile.
jgitter

1
Non mi interessa davvero discutere con te, ma JSONP utilizza tag di script per estrarre i dati da un altro dominio e quindi invia il risultato a una funzione di callback. È molto più difficile se il risultato non è json.
jgitter

1
No, non è molto più difficile. Infatti la risposta non dovrebbe essere in nessun caso JSON valido. Invece, il server dovrebbe restituire qualcosa del genere: callbackfunc(somedata). Come puoi vedere, questo non è un JSON valido. E somedatapuò essere una stringa, un numero o qualunque cosa tu voglia che sia.
Ray Nicholus

1
Sto usando Postman e lì i metodi di richiesta vengono inviati correttamente (es. "PUT", "DELETE", ecc.). Ma quando provo a farlo dal mio codice, li mando sempre con il metodo di richiesta OPZIONI. Non ho idea di come possa farlo Postman.
ErwinGO

1

Se è possibile, passare i parametri tramite GET / POST regolare con un nome diverso e lasciare che il codice lato server lo gestisca.

Ho avuto un problema simile con il mio proxy per bypassare CORS e ho ricevuto lo stesso errore di POST-> OPTION in Chrome. Era l' Authorizationheader nel mio caso ( "x-li-format"e "X-UserName"qui nel tuo caso.) Ho finito per passarlo in un formato fittizio (ad esempio AuthorizatinJackin GET) e ho cambiato il codice per il mio proxy per trasformarlo in un'intestazione quando effettuo la chiamata alla destinazione . Eccolo in PHP:

if (isset($_GET['AuthorizationJack'])) {
    $request_headers[] = "Authorization: Basic ".$_GET['AuthorizationJack'];
}

1

Nel mio caso sto chiamando un'API ospitata da AWS (API Gateway). L'errore si è verificato quando ho provato a chiamare l'API da un dominio diverso dal dominio dell'API. Poiché sono il proprietario dell'API, ho abilitato CORS per l'ambiente di test, come descritto nella documentazione di Amazon .

In produzione questo errore non si verificherà, poiché la richiesta e l'API saranno nello stesso dominio.

Spero possa essere d'aiuto!


0

Alla risposta di @Dark Falcon, me ne sono occupata semplicemente .

Nel mio caso, sto utilizzando il server node.js e creo una sessione se non esiste. Poiché il metodo OPTIONS non contiene i dettagli della sessione, ha finito per creare una nuova sessione per ogni richiesta del metodo POST.

Quindi nella mia routine dell'app per creare-sessione-se-non-esiste, ho appena aggiunto un controllo per vedere se il metodo è OPTIONS, e in tal caso, salta semplicemente la parte di creazione della sessione:

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


0

Prendi in considerazione l'utilizzo di axios

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

  if(res.data.error) {

  } else { 
    doAnything( res.data )
  }

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

Ho avuto questo problema usando fetch e axios funzionava perfettamente.


5
Axios usa anche le prime OPZIONI
Skylin R

0

Ho riscontrato un problema molto simile. Ho passato quasi mezza giornata a capire perché tutto funziona correttamente in Firefox e fallisce in Chrome. Nel mio caso è stato a causa di campi duplicati (o forse digitati in modo errato) nell'intestazione della mia richiesta.


0

Usa fetch invece di XHR, quindi la richiesta non verrà pre-illuminata anche se è cross-domain.


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

il contentType: 'text / plain; charset = utf-8 ', o solo contentType:' text / plain ', funziona per me! Saluti!!


Cosa c'entra questo con la domanda?
Corey Ogburn,

Ciao, penso che questo risolva il problema nel titolo, con questo tipo di contenuto passi il metodo OPTIONS. Saluti
David Lopes

ContentType non ha nulla a che fare con il metodo.
Corey Ogburn

So cosa stai dicendo, ma provaci. a seconda del browser il tuo tipo di contenuto può influenzare la tua richiesta e cambiare il tuo metodo!
David Lopes
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.