jQuery $ .ajax (), $ .post sendet "OPTIONS" als REQUEST_METHOD in Firefox

330

Probleme mit einem meiner Meinung nach relativ einfachen jQuery-Plugin zu haben ...

Das Plugin sollte Daten aus einem PHP-Skript über Ajax abrufen, um Optionen zu a hinzuzufügen <select>. Die Ajax-Anfrage ist ziemlich allgemein:

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

Dies scheint in Safari gut zu funktionieren. In Firefox 3.5 ist das REQUEST_TYPEauf dem Server immer 'OPTIONS' und die $ _POST-Daten werden nicht angezeigt. Apache protokolliert die Anforderung als Typ 'OPTIONEN':

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

Warum sollte dieser Ajax-Aufruf in Safari funktionieren, nicht jedoch in Firefox, und wie kann ich ihn für Firefox beheben?

Antwortheader
Datum: Mi, 08. Juli 2009 21:22:17 GMT
Server: Apache / 2.0.59 (Unix) PHP / 5.2.6 DAV / 2
X-Powered-By: PHP / 5.2.6
Inhaltslänge 46
Keep-Alive-Timeout = 15, max = 100
Verbindung bleibt erhalten
Inhaltstyp text / html

Header anfordern
Host-Bestellformular: 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
Akzeptiere Text / HTML, Anwendung / xhtml + xml, Anwendung / xml; q = 0,9, * / *; q = 0,8
Akzeptiere-Sprache en-us, en; q = 0,5
Accept-Encoding gzip, entleeren
Accept-Charset ISO-8859-1, utf-8; q = 0,7, *; q = 0,7
Keep-Alive 300
Verbindung am Leben erhalten
Herkunft http://ux.inetu.act.org
Zugriffssteuerungs-Anforderungsmethode POST
Access-Control-Request-Header x-angefordert-mit

Hier ist ein Bild der Firebug-Ausgabe:

fitzgeraldsteele
quelle
Können Sie die Firebug-Antwort posten und Header anfordern? Ich erhalte keine Fehlermeldung, wenn ich ähnlichen Code in Firefox ausführe.
MitMaro
Header-Informationen und ein Bild von Firebug hinzugefügt.
Fitzgeraldsteele
Hatte gerade das gleiche Problem bei der Implementierung eines eingebetteten Webservers. Danke, dass Sie gefragt haben :)
Robert Gould
Wenn Sie nach einer Java JAX-RS-Lösung suchen, finden Sie hier: Access-Control-Allow-Origin
Tobias Sarnow
Das Verhalten von Firefox scheint sich jetzt geändert zu haben? Ich bekomme keine Optionsanfragen.
Buge

Antworten:

169

Der Grund für den Fehler ist dieselbe Ursprungsrichtlinie. Sie können nur XMLHTTPRequests für Ihre eigene Domain durchführen. Überprüfen Sie, ob Sie stattdessen einen JSONP- Rückruf verwenden können:

$.getJSON( 'http://<url>/api.php?callback=?', function ( data ) { alert ( data ); } );
Jonas Skovmand
quelle
26
Warum ist Firefox der einzige Browser, der dies tut? Ich möchte einen Beitrag nicht bekommen.
Maslow
11
Crossite-POST: Kennt jemand eine Lösung, um einen POST mit application / json als Inhaltstyp durchzuführen?
schoetbi
13
Was genau ist die Lösung?
Nik So
3
Auch hier nach einer Lösung zu suchen und getJSON anstelle von Ajax Call zu verwenden, reicht für mich nicht aus, da es viel eingeschränkter ist.
Timo Wallenius
1
@schoetbi dafür müssen Sie CORS verwenden, das in neueren Browsern gut unterstützt wird ... eingeschränkte Unterstützung in IE8-9 und serverseitige Unterstützung.
Tracker1
57

Ich habe den folgenden Code auf der Django-Seite verwendet, um die OPTIONS-Anforderung zu interpretieren und die erforderlichen Access-Control-Header festzulegen. Danach begannen meine domänenübergreifenden Anfragen von Firefox zu funktionieren. Wie bereits erwähnt, sendet der Browser zuerst die OPTIONS-Anfrage und unmittelbar danach den 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":
        # ... 

Bearbeiten: Es scheint, dass Sie zumindest in einigen Fällen auch die gleichen Access-Control-Header zur tatsächlichen Antwort hinzufügen müssen. Dies kann etwas verwirrend sein, da die Anforderung erfolgreich zu sein scheint, Firefox den Inhalt der Antwort jedoch nicht an das Javascript weiterleitet.

Juha Palomäki
quelle
Ihre Bearbeitung der tatsächlichen POST / GET-Antwort ist etwas beängstigend. Wenn jemand dies bestätigen kann, dann lassen Sie es uns bitte hier wissen!
Arjan
Ich weiß nicht, ob es sich um einen Fehler oder eine Funktion handelt, aber es scheint, dass es auch jemand anderes bemerkt hat. Siehe zum Beispiel kodemaniak.de/?p=62 und suchen Sie nach "leerer Antwortkörper"
Juha Palomäki
2
Es gibt einen Unterschied zwischen einfachen Anfragen und solchen, die Preflight benötigen. Ihre "Lösung" funktioniert nur mit Preflight-Anfragen, daher ist es keine echte Lösung. Immer wenn Sie einen "Origin:" - Header in den Anforderungsheadern erhalten, sollten Sie mit dieser Erlaubnis antworten.
Odinho - Velmont
1
Ich glaube, der Header Access-Control-Allow-Headerssollte den Wert enthalten x-csrf-token, nicht x-csrftoken.
JellicleCat
16

Dieser Artikel im Mozilla Developer Center beschreibt verschiedene domänenübergreifende Anforderungsszenarien. Der Artikel scheint darauf hinzuweisen, dass eine POST-Anfrage mit dem Inhaltstyp 'application / x-www-form-urlencoded' als 'einfache Anfrage' gesendet werden sollte (ohne 'Preflight'-OPTIONS-Anfrage). Ich habe jedoch festgestellt, dass Firefox die OPTIONS-Anforderung gesendet hat, obwohl mein POST mit diesem Inhaltstyp gesendet wurde.

Ich konnte dies zum Laufen bringen, indem ich auf dem Server einen Optionsanforderungshandler erstellte, der den Antwortheader 'Access-Control-Allow-Origin' auf '*' setzte. Sie können restriktiver sein, indem Sie eine bestimmte Einstellung festlegen , z. B. " http://someurl.com ". Ich habe auch gelesen, dass Sie angeblich eine durch Kommas getrennte Liste mehrerer Ursprünge angeben können, aber ich konnte dies nicht zum Laufen bringen.

Sobald Firefox die Antwort auf die OPTIONS-Anforderung mit einem akzeptablen Wert für "Access-Control-Allow-Origin" erhält, sendet es die POST-Anforderung.

Mike C.
quelle
15

Ich habe dieses Problem mit einer vollständig auf Apache basierenden Lösung behoben. In meinem vhost / htaccess habe ich folgenden Block eingefügt:

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

Sie benötigen den letzten Teil möglicherweise nicht, je nachdem, was passiert, wenn Apache Ihr Zielskript ausführt. Der letzte Teil geht an die freundlichen ServerFault-Leute .

Mark McDonald
quelle
Ihre Antwort hat mir geholfen, aber wenn Sie eine Logik hinter CORS benötigen, lässt sich diese nicht vollständig lösen.
Ratata Tata
10

Dieses PHP oben im antwortenden Skript scheint zu funktionieren. (Mit Firefox 3.6.11. Ich habe noch nicht viele Tests durchgeführt.)

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);
}
Chad Clark
quelle
Dies könnte eine Frage des Geschmacks, aber immer diese Antwort - Header zu senden (auch für GET, POST, ...) ist ein bisschen zu viel nach meinem Geschmack. (Und ich frage mich, ob das Senden immer den Spezifikationen entspricht?)
Arjan
3
Wickeln Sie es in if ($ _ SERVER ['HTTP_ORIGIN']). Wenn dieser Header vorhanden ist, handelt es sich um eine CORS-Anfrage. Wenn nicht, müssen Sie nichts senden.
Odinho - Velmont
7

Ich hatte das gleiche Problem beim Senden von Anfragen an Google Maps, und die Lösung ist mit jQuery 1.5 recht einfach - für die Verwendung von Datentypen dataType: "jsonp"

Slawisch
quelle
12
Inkompatibel mit der Methode POST.
Pavel Vlasov
1
Es funktioniert mit einer GET-Methode, ist aber eine sehr begrenzte Lösung. Auf diese Weise können Sie beispielsweise keine Antwort mit einem bestimmten Header einschließlich eines Tokens zurücksenden.
Svassr
6

Culprit ist eine Preflight-Anfrage mit der OPTIONS-Methode

Für HTTP-Anforderungsmethoden, die Nebenwirkungen auf Benutzerdaten verursachen können (insbesondere für andere HTTP-Methoden als GET oder für die POST-Verwendung mit bestimmten MIME-Typen), schreibt die Spezifikation vor, dass Browser die Anforderung "vorfliegen" und unterstützte Methoden von der Server mit einer HTTP-OPTIONS-Anforderungsmethode und dann nach "Genehmigung" durch den Server die tatsächliche Anforderung mit der tatsächlichen HTTP-Anforderungsmethode senden.

Webspezifikationen beziehen sich auf: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Ich habe das Problem durch Hinzufügen der folgenden Zeilen in Nginx conf behoben.

    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;
                }
    }
Thinkhy
quelle
1
Diese Antwort ist sehr hilfreich, danke. Die Tatsache, dass der Browser eine Preflight-Anfrage mit einer OPTIONS-Methode sendet, ist nicht offensichtlich.
Normangorman
4

Ich habe Quelle 1.3.2 durchgesehen. Bei Verwendung von JSONP wird die Anforderung durch dynamisches Erstellen eines SCRIPT-Elements gestellt, das über die Same-Domain-Richtlinie des Browsers hinausgeht. Natürlich können Sie keine POST-Anfrage mit einem SCRIPT-Element stellen, der Browser würde das Ergebnis mit GET abrufen.

Wenn Sie einen JSONP-Aufruf anfordern, wird das SCRIPT-Element nicht generiert, da dies nur erfolgt, wenn der Typ des AJAX-Aufrufs auf GET festgelegt ist.

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


quelle
3

Wir hatten ein solches Problem mit ASP.Net. Unser IIS hat einen internen Serverfehler zurückgegeben, als versucht wurde, eine jQuery auszuführen $.post, um HTML-Inhalte abzurufen, da PageHandlerFactory nur GET,HEAD,POST,DEBUGVerben beantworten durfte . Sie können diese Einschränkung also ändern, indem Sie das Verb "OPTIONEN" zur Liste hinzufügen oder "Alle Verben" auswählen.

Sie können dies in Ihrem IIS-Manager ändern, indem Sie Ihre Website auswählen und dann Handlerzuordnungen auswählen. Doppelklicken Sie in Ihrer PageHandlerFactory für * .apx-Dateien nach Bedarf (wir verwenden den integrierten Anwendungspool mit Framework 4.0). Klicken Sie auf Anforderungsbeschränkungen, gehen Sie zur Registerkarte Verben und übernehmen Sie Ihre Änderung.

Jetzt $.postfunktioniert unsere Anfrage wie erwartet :)

fboiton
quelle
2

Überprüfen Sie, ob die actionURL Ihres Formulars den wwwTeil der Domain enthält, während die von Ihnen geöffnete Originalseite ohne angezeigt wird www.

In der Regel für kanonische Urls gemacht.

Ich kämpfte stundenlang, bevor ich auf diesen Artikel stieß und den Hinweis auf Cross Domain fand.

Bijay Rungta
quelle
2

Ich scheine, dass wenn o.url = 'index.php'und diese Datei existiert, in Ordnung ist und eine Erfolgsmeldung in der Konsole zurückgegeben wird. Es wird ein Fehler zurückgegeben, wenn ich url verwende:http://www.google.com

Wenn Sie eine Post-Anfrage stellen, warum nicht direkt die $ .post- Methode verwenden:

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

Es ist so viel einfacher.

Elzo Valugi
quelle
Ich habe das Gleiche damit ... dachte, ich sollte $ .ajax () verwenden, damit ich zumindest einige Debug-Informationen über die Fehlerbedingung erhalten kann ..
fitzgeraldsteele
1

Lösung hierfür ist:

  1. Verwenden Sie den Datentyp: json
  2. &callback=?zu deiner URL hinzufügen

Dies funktionierte beim Aufrufen der Facebook-API und mit Firefox. Firebug wird GETanstelle OPTIONSder oben genannten Bedingungen verwendet (beide).

Antonio Gulli
quelle
1

Eine andere Möglichkeit, das Problem zu umgehen, ist die Verwendung eines Proxy-Skripts. Diese Methode wird hier beispielsweise beschrieben

Niehztog
quelle
0

Kannst du das ohne versuchen

contentType:application/x-www-form-urlencoded

Mathias F.
quelle
Das gleiche Ergebnis, fürchte ich.
Fitzgeraldsteele
0

Versuchen Sie, die Option hinzuzufügen:

Datentyp: "json"

ScottE
quelle
2
das hat funktioniert, warum gilt json als "sicher" für domänenübergreifende anfragen?
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);

     }
});         
}
Naser Gulzade
quelle
Die Frage wurde bereits vor 6 Monaten beantwortet. Wie löst das das?
Barmar
0

Ich hatte ein ähnliches Problem beim Versuch, die Facebook-API zu verwenden.

Der einzige contentType, der die Preflighted-Anfrage nicht gesendet hat, schien nur Text / Plain zu sein ... nicht die restlichen Parameter, die hier bei Mozilla erwähnt wurden

  • Warum ist dies der einzige Browser, der dies tut?
  • Warum kennt und akzeptiert Facebook die Preflight-Anfrage nicht?

Zu Ihrer Information: Das oben erwähnte Moz-Dokument schlägt vor, dass X-Lori-Header eine Preflighted-Anfrage auslösen sollten ... das tut es nicht.

Drew
quelle
0

Sie müssen einige Arbeiten auf der Serverseite ausführen. Ich sehe, dass Sie PHP auf der Serverseite verwenden, aber die Lösung für .NET-Webanwendungen ist hier: Der Inhaltstyp kann in jQuery.ajax nicht auf 'application / json' gesetzt werden

Machen Sie dasselbe in PHP-Skript und es wird funktionieren. Einfach: Bei der ersten Anfrage fragt der Browser den Server, ob er solche Daten mit einem solchen Typ senden darf, und die zweite Anfrage ist die richtige / erlaubte.

Fanda
quelle
0

Versuchen Sie Folgendes hinzuzufügen:

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

Ich habe eine Proxy-URL verwendet, um ein ähnliches Problem zu lösen, wenn ich Daten auf meinem Apache Solr veröffentlichen möchte, der auf einem anderen Server gehostet wird. (Dies ist möglicherweise nicht die perfekte Antwort, löst aber mein Problem.)

Folgen Sie dieser URL: Mit Mode-Rewrite zum Proxying füge ich diese Zeile meiner httpd.conf hinzu:

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

Daher kann ich nur Daten an / solr senden, anstatt Daten an http: // ip: 8983 / solr / * zu senden . Dann werden Daten im selben Ursprung gebucht.

Bernice
quelle
0

Ich habe bereits diesen Code, der meine Cors-Situation in PHP gut handhabt:

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

Und es funktionierte lokal und remote einwandfrei, aber nicht für Uploads im Remote-Modus.

Es passiert etwas mit Apache / PHP ODER meinem Code. Ich habe mich nicht darum gekümmert, ihn zu durchsuchen. Wenn Sie OPTIONEN anfordern, wird mein Header mit Cors-Regeln zurückgegeben, aber mit einem Ergebnis von 302. Daher erkennt mein Browser keine akzeptable Situation.

Was ich getan habe, basierend auf der Antwort von @Mark McDonald, ist einfach diesen Code nach meinem Header zu setzen:

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

Wenn OPTIONSSie jetzt eine Anfrage stellen , werden nur der Header und das Ergebnis 202 gesendet.

Ratata Tata
quelle
-1

Bitte beachten Sie:

JSONP unterstützt nur die GET-Anforderungsmethode.

* Anfrage per Firefox senden : *

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

Oben Anfrage per OPTIONS senden (während ==> Typ: 'POST' ) !!!!

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

Aber obige Anfrage per GET senden (while ==> Typ: 'POST' ) !!!!

Wenn Sie sich in der "domänenübergreifenden Kommunikation" befinden, achten Sie darauf und seien Sie vorsichtig.

M. Namjo
quelle