Perché ricevo una richiesta OPTIONS anziché una richiesta GET?


288
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js" type="text/javascript"></script>
<script>
$.get("http://example.com/", function(data) {
     alert(data);
});
</script>

fa una richiesta OPTIONS a quell'URL e quindi il callback non viene mai chiamato con niente.

Quando non è interdominio, funziona bene.

JQuery non dovrebbe semplicemente effettuare la chiamata con un <script>nodo e quindi effettuare la richiamata quando viene caricata? Capisco che non sarò in grado di ottenere il risultato (dal momento che è cross-domain), ma va bene; Voglio solo passare la chiamata. È un bug o sto facendo qualcosa di sbagliato?


2
Potrebbe essere cos di dominio incrociato. Ad esempio, se sei nel tuo file File: // PATH_TO_WEBSITE invece di utilizzare localhost / WEBSITE_LINK
James111

Risposte:


262

Secondo MDN ,

Richieste preflight

A differenza delle richieste semplici (discusse sopra), le richieste "preflight" inviano prima un'intestazione di richiesta OPTIONS HTTP alla risorsa sull'altro dominio, al fine di determinare se la richiesta effettiva è sicura da inviare. Le richieste tra siti sono preflight in questo modo poiché potrebbero avere implicazioni per i dati dell'utente. In particolare, viene preflight una richiesta se:

  • Utilizza metodi diversi da GET o POST. Inoltre, se POST viene utilizzato per inviare dati di richiesta con un tipo di contenuto 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 usando application / xml o text / xml, la richiesta viene preflight.
  • Imposta le intestazioni personalizzate nella richiesta (ad es. La richiesta utilizza un'intestazione come X-PINGOTHER)

43
questo risolto il nostro problema, il passaggio da "application / json" a "text / plain" ha fermato la richiesta di opzioni orribili
Keeno

10
quello che non capisco è perché il browser richiede con il metodo OPTIONS solo per verificare che la richiesta effettiva sia sicura da inviare. ma in che senso? intendo che il server può anche porre restrizioni con determinate intestazioni di risposta, quindi perché questo è necessario?
Hardik,

11
@hardik Ricorda che aggiungendo CORS, stai potenzialmente accettando richieste da chiunque, in cui potrebbero manipolare i dati sul tuo server attraverso richieste (POST, PUT, DELETE ecc.). In queste situazioni, come quando si usano le intestazioni personalizzate, il browser verifica innanzitutto con il server che il server è disposto ad accettare la richiesta prima di inviarla poiché l'invio di richieste indesiderate al server potrebbe essere davvero pericoloso per i tuoi dati e, inoltre, il punto nel browser che invia payload potenzialmente grandi se il server non vuole accettarli, quindi il controllo OPZIONI pre-volo.
davidnknight,

6
@davidnknight se l'invio dei tuoi dati al server può essere pericoloso, il che significa che il server potrebbe essere compromesso, quindi ovviamente il server dannoso risponderebbe alla tua richiesta OPTIONS con "Certo, invialo dappertutto!". Come è quella sicurezza? (domanda onesta)
Matt,

3
"Le richieste di verifica preliminare non sono una questione di sicurezza. Piuttosto, sono una cosa che non cambia le regole." - Vedi la risposta a Qual è la motivazione dietro l'introduzione di richieste di verifica preliminare
FMJaguar


9

Se stai cercando di POST

Accertati dei JSON.stringifydati del modulo e invia come text/plain.

<form id="my-form" onSubmit="return postMyFormData();">
    <input type="text" name="name" placeholder="Your Name" required>
    <input type="email" name="email" placeholder="Your Email" required>
    <input type="submit" value="Submit My Form">
</form>

function postMyFormData() {

    var formData = $('#my-form').serializeArray();
    formData = formData.reduce(function(obj, item) {
        obj[item.name] = item.value;
        return obj;
    }, {});
    formData = JSON.stringify(formData);

    $.ajax({
        type: "POST",
        url: "https://website.com/path",
        data: formData,
        success: function() { ... },
        dataType: "text",
        contentType : "text/plain"
    });
}

2

Non credo che jQuery farà naturalmente una richiesta JSONP quando gli viene dato un URL del genere. Farà comunque una richiesta JSONP quando gli dirai quale argomento usare per un callback:

$.get("http://metaward.com/import/http://metaward.com/u/ptarjan?jsoncallback=?", function(data) {
     alert(data);
});

Spetta interamente allo script di ricezione utilizzare quell'argomento (che non deve essere chiamato "jsoncallback"), quindi in questo caso la funzione non verrà mai chiamata. Ma, dal momento che hai dichiarato che vuoi solo eseguire lo script su metaward.com, questo lo farebbe.


il MIO callback verrebbe comunque avvisato che l'elemento dello script è stato caricato completamente? Voglio solo assicurarmi che l'aggiornamento sia avvenuto prima di eseguire una query sull'API.
Paul Tarjan,

Lo farai se lo script ricevente supporta JSONP ed è disposto a chiamare la funzione identificata. Se lo script non fa altro che generare un blocco di dati JSON senza altri comportamenti, non sarai in grado di dire quando il caricamento è terminato. Se è essenziale sapere quando è stato completato il caricamento, è possibile prendere in considerazione l'implementazione di uno script sul proprio server che funga da proxy.
VoteyDisciple

1

In effetti, le richieste AJAX (XMLHttp) tra domini non sono consentite per motivi di sicurezza (pensate a recuperare una pagina Web "limitata" dal lato client e inviarla al server - questo sarebbe un problema di sicurezza).

L'unica soluzione alternativa sono i callback. Questo è: creazione di un nuovo oggetto script e puntamento di src al JavaScript lato-end, che è un callback con valori JSON (myFunction ({data}), myFunction è una funzione che fa qualcosa con i dati (ad esempio, memorizzandolo in una variabile).


1
giusto, ma posso caricarlo in uno <script src = ""> o <img src = ""> e il browser lo colpirà felicemente. Voglio solo sapere quando è completamente caricato in modo da poter eseguire una query per il risultato dell'importazione.
Paul Tarjan,

1

Basta cambiare "application / json" in "text / plain" e non dimenticare JSON.stringify (richiesta):

var request = {Company: sapws.dbName, UserName: username, Password: userpass};
    console.log(request);
    $.ajax({
        type: "POST",
        url: this.wsUrl + "/Login",
        contentType: "text/plain",
        data: JSON.stringify(request),

        crossDomain: true,
    });

1

Ho avuto lo stesso problema. La mia soluzione era aggiungere intestazioni al mio script PHP che sono presenti solo in ambiente dev.

Ciò consente richieste tra domini:

header("Access-Control-Allow-Origin: *");

Questo indica alla richiesta di verifica preliminare che è corretto che il client invii le intestazioni che desidera:

header("Access-Control-Allow-Headers: *");

In questo modo non è necessario modificare la richiesta.

Se nel tuo database di sviluppo sono presenti dati sensibili che potrebbero essere trapelati, potresti pensarci due volte.


1

Nel mio caso, il problema non era correlato a CORS poiché stavo emettendo un POST jQuery sullo stesso server Web. I dati erano JSON ma avevo omesso il parametro dataType: 'json'.

Non avevo (né ho aggiunto) un parametro contentType come mostrato nella risposta di David Lopes sopra.


0

Sembra che Firefox e Opera (testati anche su Mac) non apprezzino il dominio interdominio di questo (ma Safari va bene con esso).

Potrebbe essere necessario chiamare un codice lato server locale per arricciare la pagina remota.


0

Sono stato in grado di risolverlo con l'aiuto delle seguenti intestazioni

Access-Control-Allow-Origin
Access-Control-Allow-Headers
Access-Control-Allow-Credentials
Access-Control-Allow-Methods

Se sei su Nodejs, ecco il codice che puoi copiare / incollare.

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin','*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  res.header('Access-Control-Allow-Credentials', true);
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH');
  next();
});
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.