Warum erhalte ich eine OPTIONS-Anfrage anstelle einer GET-Anfrage?

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>

Es führt eine OPTIONS-Anforderung an diese URL durch, und dann wird der Rückruf nie mit irgendetwas aufgerufen.

Wenn es nicht domänenübergreifend ist, funktioniert es einwandfrei.

Sollte jQuery nicht einfach den Anruf mit einem <script>Knoten tätigen und dann den Rückruf ausführen, wenn dieser geladen ist? Ich verstehe, dass ich das Ergebnis nicht erhalten kann (da es domänenübergreifend ist), aber das ist in Ordnung. Ich möchte nur, dass der Anruf durchgeht. Ist das ein Fehler oder mache ich etwas falsch?

Paul Tarjan
quelle
2
Könnte cos von Cross Domain sein. Beispiel: Wenn Sie sich in Ihrer Datei befinden Datei: // PATH_TO_WEBSITE anstelle von localhost / WEBSITE_LINK
James111

Antworten:

262

Nach MDN ,

Preflighted Anfragen

Im Gegensatz zu einfachen Anforderungen (siehe oben) senden "Preflighted" -Anforderungen zuerst einen HTTP OPTIONS-Anforderungsheader an die Ressource in der anderen Domäne, um festzustellen, ob die tatsächliche Anforderung sicher zu senden ist. Standortübergreifende Anforderungen werden auf diese Weise vorab ausgeführt, da sie Auswirkungen auf Benutzerdaten haben können. Insbesondere wird eine Anfrage vorab geflogen, wenn:

  • Es werden andere Methoden als GET oder POST verwendet. Wenn POST zum Senden von Anforderungsdaten mit einem anderen Inhaltstyp als application / x-www-form-urlencoded, multipart / form-data oder text / plain verwendet wird, z. B. wenn die POST-Anforderung eine XML-Nutzlast an den Server sendet Wenn Sie application / xml oder text / xml verwenden, wird die Anforderung vorab ausgeführt.
  • Es werden benutzerdefinierte Header in der Anforderung festgelegt (z. B. verwendet die Anforderung einen Header wie X-PINGOTHER).
Arturgrigor
quelle
43
Dies hat unser Problem behoben. Durch den Wechsel von "application / json" zu "text / plain" wurde die schreckliche
Optionsanforderung
10
Was ich nicht verstehe, ist, warum der Browser mit der OPTIONS-Methode anfordert, nur um zu überprüfen, ob die tatsächliche Anforderung sicher zu senden ist. aber in welchem ​​Sinne? Ich meine, der Server kann auch Einschränkungen für bestimmte Antwortheader festlegen. Warum ist dies erforderlich?
Hardik
11
@hardik Denken Sie daran, dass Sie durch Hinzufügen von CORS möglicherweise Anforderungen von allen Personen annehmen, bei denen Daten auf Ihrem Server durch Anforderungen (POST, PUT, DELETE usw.) bearbeitet werden könnten. In diesen Situationen, z. B. bei der Verwendung von benutzerdefinierten Headern, überprüft der Browser zunächst beim Server, ob der Server bereit ist, die Anforderung zu akzeptieren, bevor er sie sendet, da das Senden unerwünschter Anforderungen an den Server für Ihre Daten sehr gefährlich sein kann und auch was Der Punkt im Browser, an dem potenziell große Nutzdaten gesendet werden, wenn der Server sie nicht akzeptieren möchte, daher die OPTIONEN vor dem Flug.
Davidnknight
6
@davidnknight Wenn das Senden Ihrer Daten an den Server gefährlich sein kann, was bedeutet, dass der Server möglicherweise gefährdet ist, antwortet der böswillige Server natürlich auf Ihre OPTIONS-Anfrage mit "Sicher, senden Sie alles!". Wie ist diese Sicherheit? (ehrliche Frage)
Matt
3
"Preflight-Anfragen sind keine Sicherheitssache. Sie sind vielmehr eine Sache, bei der die Regeln nicht geändert werden." - Siehe die Antwort auf Was ist die Motivation hinter der Einführung von Preflight-Anfragen
FMJaguar
9

Wenn Sie versuchen zu POSTEN

Stellen Sie sicher, dass JSON.stringifyIhre Formulardaten und senden als 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"
    });
}
Derek Soike
quelle
2

Ich glaube nicht, dass jQuery auf natürliche Weise eine JSONP-Anfrage ausführt, wenn eine solche URL angegeben wird. Es wird jedoch eine JSONP-Anforderung ausgeführt, wenn Sie angeben, welches Argument für einen Rückruf verwendet werden soll:

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

Es liegt ganz beim empfangenden Skript, dieses Argument zu verwenden (das nicht "jsoncallback" heißen muss). In diesem Fall wird die Funktion also niemals aufgerufen. Da Sie jedoch angegeben haben, dass das Skript auf metaward.com nur ausgeführt werden soll, ist dies der Fall.

VoteyDisciple
quelle
Würde MEIN Rückruf weiterhin benachrichtigt, dass das Skriptelement vollständig geladen wurde? Ich möchte nur sicherstellen, dass das Update durchgeführt wurde, bevor ich die API danach abfrage.
Paul Tarjan
Sie werden es tun, wenn das empfangende Skript JSONP unterstützt und bereit ist, die von Ihnen identifizierte Funktion aufzurufen. Wenn das Skript nur einen Block von JSON-Daten ohne anderes Verhalten generiert, können Sie nicht erkennen, wann das Laden abgeschlossen ist. Wenn Sie wissen möchten, wann das Laden abgeschlossen ist, können Sie ein Skript auf Ihrem eigenen Server implementieren, das als Proxy fungiert.
VoteyDisciple
1

Domänenübergreifende AJAX-Anforderungen (XMLHttp) sind aus Sicherheitsgründen nicht zulässig (denken Sie daran, eine "eingeschränkte" Webseite vom Client abzurufen und an den Server zurückzusenden - dies wäre ein Sicherheitsproblem).

Die einzige Problemumgehung sind Rückrufe. Dies ist: Erstellen eines neuen Skriptobjekts und Zeigen des src auf das endseitige JavaScript, bei dem es sich um einen Rückruf mit JSON-Werten handelt (myFunction ({data}). MyFunction ist eine Funktion, die etwas mit den Daten tut (z. B. das Speichern) in einer Variablen).

Adrián Navarro
quelle
1
Richtig, aber ich kann es in ein <script src = ""> oder <img src = ""> laden und der Browser wird es gerne treffen. Ich möchte nur wissen, wann es vollständig geladen ist, damit ich das Ergebnis des Imports abfragen kann.
Paul Tarjan
1

Ändern Sie einfach die "application / json" in "text / plain" und vergessen Sie nicht die JSON.stringify (Anfrage):

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,
    });
David Lopes
quelle
1

Ich hatte das gleiche Problem. Mein Fix bestand darin, meinem PHP-Skript Header hinzuzufügen, die nur in einer Entwicklungsumgebung vorhanden sind.

Dies ermöglicht domänenübergreifende Anforderungen:

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

Dies teilt der Preflight-Anforderung mit, dass der Client alle gewünschten Header senden kann:

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

Auf diese Weise muss die Anforderung nicht geändert werden.

Wenn Ihre Entwicklungsdatenbank vertrauliche Daten enthält, die möglicherweise durchgesickert sind, sollten Sie dies zweimal überlegen.

Fivebit
quelle
1

In meinem Fall hatte das Problem nichts mit CORS zu tun, da ich einen jQuery-POST an denselben Webserver ausgegeben habe. Die Daten waren JSON, aber ich hatte den Parameter dataType: 'json' weggelassen.

Ich hatte keinen contentType-Parameter (und habe ihn auch nicht hinzugefügt), wie in der obigen Antwort von David Lopes gezeigt.

GarDavis
quelle
0

Es sieht so aus, als ob Firefox und Opera (auch auf dem Mac getestet) die Domänenübergreifende nicht mögen (aber Safari ist damit einverstanden).

Möglicherweise müssen Sie einen lokalen serverseitigen Code aufrufen, um die Remote-Seite zu locken.

halloandre
quelle
0

Ich konnte es mit Hilfe der folgenden Überschriften beheben

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

Wenn Sie auf Nodejs sind, finden Sie hier den Code, den Sie kopieren / einfügen können.

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