Eine CORS POST-Anfrage funktioniert mit einfachem JavaScript, aber warum nicht mit jQuery?

87

Ich versuche, eine Cross Origin-Post-Anfrage zu stellen, und ich habe sie so einfach zum JavaScriptLaufen gebracht:

var request = new XMLHttpRequest();
var params = "action=something";
request.open('POST', url, true);
request.onreadystatechange = function() {if (request.readyState==4) alert("It worked!");};
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.setRequestHeader("Content-length", params.length);
request.setRequestHeader("Connection", "close");
request.send(params);

Aber ich würde es gerne benutzen jQuery, aber ich kann es nicht zum Laufen bringen. Das versuche ich:

$.ajax(url, {
    type:"POST",
    dataType:"json",
    data:{action:"something"}, 
    success:function(data, textStatus, jqXHR) {alert("success");},
    error: function(jqXHR, textStatus, errorThrown) {alert("failure");}
});

Dies führt zu einem Fehler. Wenn jemand weiß, warum jQueryes nicht funktioniert, lassen Sie es uns bitte alle wissen. Vielen Dank.

(Ich verwende jQuery1.5.1 und Firefox 4.0 und mein Server antwortet mit einem richtigen Access-Control-Allow-OriginHeader.)

Magmatisch
quelle
Dies war die Lösung für mich (verwenden Sie Javascript's XMLHttpRequest), während ich mit CORS-Problemen mit Ionic Framework 3
konfrontiert war

Antworten:

72

UPDATE: Wie TimK betonte, wird dies mit jquery 1.5.2 nicht mehr benötigt. Wenn Sie jedoch benutzerdefinierte Header hinzufügen oder die Verwendung von Anmeldeinformationen (Benutzername, Kennwort oder Cookies usw.) zulassen möchten, lesen Sie weiter.


Ich glaube ich habe die Antwort gefunden! (4 Stunden und viel Fluchen später)

//This does not work!!
Access-Control-Allow-Headers: *

Sie müssen alle Header, die Sie akzeptieren, manuell angeben (zumindest war dies bei FF 4.0 und Chrome 10.0.648.204 der Fall).

Die $ .ajax-Methode von jQuery sendet den Header "x-request-with" für alle domänenübergreifenden Anforderungen (ich denke, es ist die einzige domänenübergreifende Methode).

Der fehlende Header, der zur Beantwortung der OPTIONS-Anforderung benötigt wird, lautet also:

//no longer needed as of jquery 1.5.2
Access-Control-Allow-Headers: x-requested-with

Wenn Sie nicht "einfache" Header übergeben, müssen Sie diese in Ihre Liste aufnehmen (ich sende noch eine):

//only need part of this for my custom header
Access-Control-Allow-Headers: x-requested-with, x-requested-by

Um alles zusammenzufassen, hier ist mein PHP:

// * wont work in FF w/ Allow-Credentials
//if you dont need Allow-Credentials, * seems to work
header('Access-Control-Allow-Origin: http://www.example.com');
//if you need cookies or login etc
header('Access-Control-Allow-Credentials: true');
if ($this->getRequestMethod() == 'OPTIONS')
{
  header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
  header('Access-Control-Max-Age: 604800');
  //if you need special headers
  header('Access-Control-Allow-Headers: x-requested-with');
  exit(0);
}
Will Mason
quelle
5
Beachten Sie, dass jQuery 1.5.2 sein Verhalten geändert hat. Es wird kein "X-Requested-With" -Header mehr hinzugefügt, sodass dies möglicherweise kein Problem mehr darstellt. blog.jquery.com/2011/03/31/jquery-152-released (Bug 8423)
Magmatic
1
@ TimK, du hast recht! Ich habe nicht bemerkt, dass sie 1.5.2 veröffentlichen. Dies funktioniert jedoch auch, wenn Sie es vor dem Flug benötigen. Ich habe meine Antwort aktualisiert.
Will Mason
Also bin ich verwirrt. Sie mussten trotzdem ein PHP-Zwischenskript schreiben? Sie müssen sich also keine Sorgen um die Verwendung von Ajax machen, oder? Oder fehlt mir etwas? Gibt es keine reine JavaScript-Lösung?
Elisabeth
1
@Elisabeth Diese Methode funktioniert nur, wenn Sie das angeforderte Ziel steuern ... es ist KEIN Zwischenskript. Es ist die Spitze unseres PHP unseres gewünschten Standorts. Ist das sinnvoller?
Will Mason
2
Ja! danke Will. Ich dachte, Sie könnten alles von der Client-Seite aus steuern, aber es hört sich so an, als müssten Sie beide Enden kontrollieren.
Elisabeth
18

Eine andere Möglichkeit besteht darin, dataType: jsondass JQuery durch die Einstellung den Content-Type: application/jsonHeader sendet . Dies wird von CORS als nicht standardmäßiger Header betrachtet und erfordert eine CORS-Preflight-Anforderung. Also ein paar Dinge zum Ausprobieren:

1) Versuchen Sie, Ihren Server so zu konfigurieren, dass die richtigen Preflight-Antworten gesendet werden. Dies erfolgt in Form von zusätzlichen Headern wie Access-Control-Allow-Methodsund Access-Control-Allow-Headers.

2) Löschen Sie die dataType: jsonEinstellung. JQuery sollte verlangen Content-Type: application/x-www-form-urlencodedstandardmäßig, aber nur um sicher zu sein, können Sie ersetzen dataType: jsonmitcontentType: 'application/x-www-form-urlencoded'

Monsur
quelle
Danke für die Ideen. Ich habe versucht Einstellung nicht datatype, und wenn er sein application/x-www-form-urlencodedund sogar text/plain. Und ich habe versucht, einen Antwortheader von Access-Control-Allow-Methods "POST, GET, OPTIONS"Nothing funktioniert hinzuzufügen .
Magmatisch
Können Sie in der JavaScript-Fehlerkonsole (oder in der Firebug-Konsole) nachsehen, ob während der Anforderung Fehler aufgetreten sind? Wenn Sie wissen, wie Wireshark verwendet wird, können Sie damit auch die tatsächlichen HTTP-Anforderungen anzeigen, die über die Leitung gesendet werden.
Monsur
1
"Eine andere Möglichkeit besteht darin, dass durch das Festlegen von dataType: json JQuery den Header Content-Type: application / json sendet." - Dies ist nicht der Fall. dataTypebeeinflusst den AcceptAnforderungsheader, nicht jedoch den Content-TypeAnforderungsheader.
Quentin
9

Sie senden "params" in js: request.send(params);

aber "Daten" in jquery ". Sind Daten definiert?: data:data,

Außerdem haben Sie einen Fehler in der URL:

$.ajax( {url:url,
         type:"POST",
         dataType:"json",
         data:data, 
         success:function(data, textStatus, jqXHR) {alert("success");},
         error: function(jqXHR, textStatus, errorThrown) {alert("failure");}
});

Sie mischen die Syntax mit der für $ .post


Update : Ich habe basierend auf der Monsur-Antwort gegoogelt und festgestellt, dass Sie etwas hinzufügen müssen Access-Control-Allow-Headers: Content-Type(unten ist der vollständige Absatz).

http://metajack.im/2010/01/19/crossdomain-ajax-for-xmpp-http-binding-made-easy/

Wie funktioniert CORS?

CORS funktioniert sehr ähnlich wie die Datei crossdomain.xml von Flash. Grundsätzlich sendet der Browser eine domänenübergreifende Anfrage an einen Dienst und setzt den HTTP-Header Origin auf den anfordernden Server. Der Dienst enthält einige Header wie Access-Control-Allow-Origin, um anzugeben, ob eine solche Anforderung zulässig ist.

Für die BOSH-Verbindungsmanager reicht es aus, anzugeben, dass alle Ursprünge zulässig sind, indem der Wert von Access-Control-Allow-Origin auf * gesetzt wird. Der Content-Type-Header muss auch im Header Access-Control-Allow-Headers auf der weißen Liste stehen.

Schließlich wird für bestimmte Arten von Anforderungen, einschließlich BOSH-Verbindungsmanageranforderungen, die Berechtigungsprüfung vor dem Flug durchgeführt. Der Browser führt eine OPTIONS-Anforderung durch und erwartet, dass einige HTTP-Header zurückgegeben werden, die angeben, welche Ursprünge zulässig sind, welche Methoden zulässig sind und wie lange diese Autorisierung dauert. Hier sind zum Beispiel die Punjab- und Ejabberd-Patches, die ich für OPTIONEN zurückgegeben habe:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type 
Access-Control-Max-Age: 86400
Aleadam
quelle
1
Es tut uns leid. Ja. var data = {action:"something"}
Magmatisch
Sie können die Syntax für beide Funktionen hier vergleichen: api.jquery.com/jQuery.post
Aleadam
Ich habe es gerade mit der URL in den Einstellungen versucht, aber das gleiche Problem. Die .ajax-Funktion kann in beide Richtungen ausgeführt werden.
Magmatisch
Ich hatte bereits zwei dieser Header. Ich habe die anderen beiden hinzugefügt. Immer noch "Fehler" mit jQuery. Das einfache Javascript funktioniert immer noch.
Magmatisch
Das Letzte, woran ich denken kann, ist, api.jquery.com/jQuery.ajaxSetup zu verwenden, jQuery.ajaxSetup({'beforeSend': function(xhr) {xhr.setRequestHeader(string, string)}})um die verschiedenen gesendeten Header festzulegen und damit zu spielen (ein Beispiel für Rails hier: railscasts.com/episodes/136-jquery )
Aleadam
1

Cors ändern die Anforderungsmethode, bevor sie abgeschlossen ist, von POST zu OPTIONS, sodass Ihre Post-Daten nicht gesendet werden. Die Art und Weise, wie dieses Cors-Problem behandelt wurde, besteht darin, die Anforderung mit Ajax auszuführen, das die OPTIONS-Methode nicht unterstützt. Beispielcode:

        $.ajax({
            type: "POST",
            crossdomain: true,
            url: "http://localhost:1415/anything",
            dataType: "json",
            data: JSON.stringify({
                anydata1: "any1",
                anydata2: "any2",
            }),
            success: function (result) {
                console.log(result)
            },
            error: function (xhr, status, err) {
                console.error(xhr, status, err);
            }
        });

mit diesen Headern auf c # Server:

                    if (request.HttpMethod == "OPTIONS")
                    {
                          response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
                          response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
                          response.AddHeader("Access-Control-Max-Age", "1728000");
                    }
                    response.AppendHeader("Access-Control-Allow-Origin", "*");
Lucas silva de souza sandim
quelle
-2

Ändern Sie Ihre JQuery wie folgt:

$.ajax({
            url: someurl,
            contentType: 'application/json',
            data: JSONObject,
            headers: { 'Access-Control-Allow-Origin': '*' }, //add this line
            dataType: 'json',
            type: 'POST',                
            success: function (Data) {....}
});
Soma Sarkar
quelle
Warum sollte ich meine Ajax-Callas synchronisieren wollen?
Radko Dinev
contentType: 'application/json', data: JSONObject,- Der Server erwartet kein JSON, daher ist das Senden von JSON nicht sinnvoll. Auch gibt es nicht so etwas wie ein JSON - Objekt .
Quentin
1
headers: { 'Access-Control-Allow-Origin': '*' }, //add this line- Mach das niemals .Access-Control-Allow-Originist eine Antwort - Header, kein Request - Header. Bestenfalls wird dies nichts tun. Im schlimmsten Fall wird die Anforderung von einer einfachen Anforderung in eine vorab geflogene Anforderung konvertiert, was die Bearbeitung auf dem Server immer schwieriger macht.
Quentin