So funktioniert eine CORS-Post-Anfrage (Cross-Origin Resource Sharing)

216

Ich habe einen Computer auf meinem lokalen LAN (machineA) mit zwei Webservern. Die erste ist die in XBMC (auf Port 8080) integrierte und zeigt unsere Bibliothek an. Der zweite Server ist ein CherryPy-Python-Skript (Port 8081), mit dem ich bei Bedarf eine Dateikonvertierung auslöse. Die Dateikonvertierung wird durch eine AJAX POST-Anforderung von der vom XBMC-Server bereitgestellten Seite ausgelöst.

  • Gehe zu http: // machineA: 8080 welche zeigt Bibliothek
  • Bibliothek wird angezeigt
  • Der Benutzer klickt auf den Link 'Konvertieren', der den folgenden Befehl ausgibt:

jQuery Ajax-Anfrage

$.post('http://machineA:8081', {file_url: 'asfd'}, function(d){console.log(d)})
  • Der Browser gibt eine HTTP OPTIONS-Anforderung mit den folgenden Headern aus.

Request Header - OPTIONEN

Host: machineA:8081
User-Agent: ... Firefox/4.01
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Origin: http://machineA:8080
Access-Control-Request-Method: POST
Access-Control-Request-Headers: x-requested-with
  • Der Server antwortet wie folgt:

Antwortheader - OPTIONEN (STATUS = 200 OK)

Content-Length: 0
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 1728000
Server: CherryPy/3.2.0
Date: Thu, 21 Apr 2011 22:40:29 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Content-Type: text/html;charset=ISO-8859-1
  • Das Gespräch wird dann beendet. Der Browser sollte theoretisch eine POST-Anfrage ausgeben, da der Server mit den richtigen (?) CORS-Headern geantwortet hat (Access-Control-Allow-Origin: *).

Zur Fehlerbehebung habe ich auch den gleichen Befehl $ .post von http://jquery.com ausgegeben . Hier bin ich ratlos, von jquery.com aus funktioniert die Post-Anfrage, eine OPTIONS-Anfrage wird gefolgt von einem POST gesendet. Die Header dieser Transaktion sind unten aufgeführt.

Request Header - OPTIONEN

Host: machineA:8081
User-Agent: ... Firefox/4.01
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Origin: http://jquery.com
Access-Control-Request-Method: POST

Antwortheader - OPTIONEN (STATUS = 200 OK)

Content-Length: 0
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 1728000
Server: CherryPy/3.2.0
Date: Thu, 21 Apr 2011 22:37:59 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Content-Type: text/html;charset=ISO-8859-1

Request Header - POST

Host: machineA:8081
User-Agent: ... Firefox/4.01
Accept: */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://jquery.com/
Content-Length: 12
Origin: http://jquery.com
Pragma: no-cache
Cache-Control: no-cache

Antwortheader - POST (STATUS = 200 OK)

Content-Length: 32
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 1728000
Server: CherryPy/3.2.0
Date: Thu, 21 Apr 2011 22:37:59 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Content-Type: application/json

Ich kann nicht herausfinden, warum dieselbe Anfrage von einer Site aus funktioniert, aber nicht von der anderen. Ich hoffe, dass jemand darauf hinweisen kann, was mir fehlt. Danke für Ihre Hilfe!

James
quelle
Wird CORS benötigt, wenn sich beide Webserver auf demselben Computer befinden?
jdigital
8
Nach meinem besten Wissen handelt es sich aufgrund des unterschiedlichen Ports um eine CORS-Anfrage. Außerdem zeigt die OPTIONS-Anfrage an, dass der Browser sie als CORS-Anfrage behandelt
James

Antworten:

157

Ich bin schließlich auf diesen Link gestoßen: " Eine CORS POST-Anfrage funktioniert mit einfachem Javascript, aber warum nicht mit jQuery? ", Der feststellt, dass jQuery 1.5.1 das hinzufügt

 Access-Control-Request-Headers: x-requested-with

Header zu allen CORS-Anfragen. jQuery 1.5.2 macht das nicht. Entsprechend der gleichen Frage wird auch ein Server-Antwortheader von festgelegt

Access-Control-Allow-Headers: *

lässt nicht zu, dass die Antwort fortgesetzt wird. Sie müssen sicherstellen, dass der Antwortheader speziell die erforderlichen Header enthält. dh:

Access-Control-Allow-Headers: x-requested-with 
James
quelle
70

ANFRAGE:

 $.ajax({
            url: "http://localhost:8079/students/add/",
            type: "POST",
            crossDomain: true,
            data: JSON.stringify(somejson),
            dataType: "json",
            success: function (response) {
                var resp = JSON.parse(response)
                alert(resp.status);
            },
            error: function (xhr, status) {
                alert("error");
            }
        });

ANTWORT:

response = HttpResponse(json.dumps('{"status" : "success"}'))
response.__setitem__("Content-type", "application/json")
response.__setitem__("Access-Control-Allow-Origin", "*")

return response
Hassan Zaheer
quelle
3
Wenn der Server keinen Cross-Origin akzeptiert, muss die Crossdomain = true das Problem nicht lösen. Verwenden Sie dataType: "jsonp" und setzen Sie den Rückruf wie jsonpCallback: "response". Dies ist die bessere Idee. Siehe auch: api.jquery.com/jquery.ajax
BonifatiusK
14

Ich habe mein eigenes Problem bei der Verwendung der Google Distance Matrix API gelöst, indem ich meinen Anforderungsheader mit Jquery Ajax festgelegt habe. Schauen Sie unten.

var settings = {
          'cache': false,
          'dataType': "jsonp",
          "async": true,
          "crossDomain": true,
          "url": "https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins=place_id:"+me.originPlaceId+"&destinations=place_id:"+me.destinationPlaceId+"&region=ng&units=metric&key=mykey",
          "method": "GET",
          "headers": {
              "accept": "application/json",
              "Access-Control-Allow-Origin":"*"
          }
      }

      $.ajax(settings).done(function (response) {
          console.log(response);

      });

Beachten Sie, was ich bei den Einstellungen hinzugefügt habe
**

"headers": {
          "accept": "application/json",
          "Access-Control-Allow-Origin":"*"
      }

**
Ich hoffe das hilft.

Miracool
quelle
Dies ist die einzige Lösung, die für uns funktioniert hat, ohne etwas auf der Serverseite zu ändern ... Danke Miracool
Timsta
1
@ Timsta, bin froh, dass mein Vorschlag für Sie funktioniert hat. Dankbar, auch Überlauf zu stapeln. Ich wünsche ihnen einen wunderbaren Tag.
Miracool
13

Ich habe einige Zeit gebraucht, um die Lösung zu finden.

Wenn Ihre Server - Antwort korrekt , und die Anforderung ist das Problem, sollen Sie hinzufügen , withCredentials: trueauf den xhrFieldsin der Anfrage:

$.ajax({
    url: url,
    type: method,
    // This is the important part
    xhrFields: {
        withCredentials: true
    },
    // This is the important part
    data: data,
    success: function (response) {
        // handle the response
    },
    error: function (xhr, status) {
        // handle errors
    }
});

Hinweis: jQuery> = 1.5.1 ist erforderlich

Dekel
quelle
jquery version ok? Haben Sie die withCredentials: true? Sind Sie sicher, dass Sie die entsprechenden Überschriften haben?
Dekel
Ja, 1withCredential: true , jquery version: 3.2.1`. In der Tat arbeitet es durch Postboten, aber es geht nicht durch Chrome-Browser
Mox Shah
1
Ich bin mir fast sicher, dass Postbote keine CORS-Probleme haben sollten, da es sich nicht um einen Browser handelt und das Verhalten anders ist. Sind Sie sicher, dass Sie die richtigen und relevanten Header vom Server an den Client senden lassen? Nochmals - beachten Sie, dass diese Änderung nicht ausreicht. Sie müssen sicherstellen, dass der Server mit den richtigen Headern antwortet .
Dekel
Können Sie mir sagen, welche Antwortheader auf dem Server erforderlich sind?
Mox Shah
@MoxShah das ist eine ganz andere Frage und es gibt viele Ressourcen dort :)
Dekel
9

Nun, ich hatte ein paar Wochen mit diesem Problem zu kämpfen.

Der einfachste, konformste und nicht hackigste Weg, dies zu tun, ist wahrscheinlich die Verwendung einer Anbieter-JavaScript-API, die keine browserbasierten Aufrufe ausführt und Cross Origin-Anfragen verarbeiten kann.

ZB Facebook JavaScript API und Google JS API.

Falls Ihr API-Anbieter nicht aktuell ist und den Cross Origin Resource Origin-Header '*' in seiner Antwort nicht unterstützt und keine JS-API hat (Ja, ich spreche von Ihnen, Yahoo), stehen Ihnen drei Optionen zur Verfügung:

  1. Verwenden Sie jsonp in Ihren Anfragen, um Ihrer URL eine Rückruffunktion hinzuzufügen, mit der Sie Ihre Antwort bearbeiten können. Vorsichtsmaßnahme: Dadurch wird die Anforderungs-URL geändert, sodass Ihr API-Server für die Verarbeitung von? Callback = am Ende der URL ausgestattet sein muss.

  2. Senden Sie die Anforderung an Ihren API-Server, der von Ihnen gesteuert wird und sich entweder in derselben Domäne wie der Client befindet oder für den die Cross Origin-Ressourcenfreigabe aktiviert ist, von wo aus Sie die Anforderung an den API-Server eines Drittanbieters weiterleiten können.

  3. Wahrscheinlich am nützlichsten in Fällen, in denen Sie OAuth-Anfragen stellen und Benutzerinteraktionen durchführen müssen Haha! window.open('url',"newwindowname",'_blank', 'toolbar=0,location=0,menubar=0')

Pritam Roy
quelle
4

Die Verwendung in Kombination mit Laravel löste mein Problem. Fügen Sie diesen Header einfach zu Ihrer JQuery-Anfrage hinzu Access-Control-Request-Headers: x-requested-withund stellen Sie sicher, dass für Ihre serverseitige Antwort dieser Header festgelegt ist Access-Control-Allow-Headers: *.

MK
quelle
6
Es gibt keinen Grund, der Anforderung manuell CORS-Header hinzuzufügen. Der Browser fügt der Anfrage für Sie immer die CORS-Header der Requisite hinzu.
Ray Nicholus
1
Die eigentliche Herausforderung besteht darin, den Server dazu zu bringen, mit einem korrekten Access-Control-Allow-Headersund einem JQ zu antworten, der korrekt ist Access-Control-Request-Headers(plus alle, die Sie per Code hinzufügen), von denen keiner Platzhalter sein kann. Es ist nur ein "schlechter" Header If-None-Matcherforderlich, um den Vorflug in die Luft zu jagen, z. B. für ein bedingtes GET, wenn auf dem Server dieser nicht aufgeführt ist.
Flucht-llc
1

Aus irgendeinem Grund wurde eine Frage zu GET-Anfragen mit dieser zusammengeführt, daher werde ich hier darauf antworten.

Diese einfache Funktion erhält asynchron eine HTTP-Statusantwort von einer CORS-fähigen Seite. Wenn Sie es ausführen, werden Sie feststellen, dass nur eine Seite mit den richtigen Kopfzeilen einen 200-Status zurückgibt, wenn über XMLHttpRequest zugegriffen wird - unabhängig davon, ob GET oder POST verwendet wird. Auf der Clientseite kann nichts unternommen werden, um dies zu umgehen, außer möglicherweise JSONP, wenn Sie nur ein JSON-Objekt benötigen.

Folgendes kann leicht geändert werden, um die im xmlHttpRequestObject-Objekt enthaltenen Daten abzurufen:

function checkCorsSource(source) {
  var xmlHttpRequestObject;
  if (window.XMLHttpRequest) {
    xmlHttpRequestObject = new XMLHttpRequest();
    if (xmlHttpRequestObject != null) {
      var sUrl = "";
      if (source == "google") {
        var sUrl = "https://www.google.com";
      } else {
        var sUrl = "https://httpbin.org/get";
      }
      document.getElementById("txt1").innerHTML = "Request Sent...";
      xmlHttpRequestObject.open("GET", sUrl, true);
      xmlHttpRequestObject.onreadystatechange = function() {
        if (xmlHttpRequestObject.readyState == 4 && xmlHttpRequestObject.status == 200) {
          document.getElementById("txt1").innerHTML = "200 Response received!";
        } else {
          document.getElementById("txt1").innerHTML = "200 Response failed!";
        }
      }
      xmlHttpRequestObject.send();
    } else {
      window.alert("Error creating XmlHttpRequest object. Client is not CORS enabled");
    }
  }
}
<html>
<head>
  <title>Check if page is cors</title>
</head>
<body>
  <p>A CORS-enabled source has one of the following HTTP headers:</p>
  <ul>
    <li>Access-Control-Allow-Headers: *</li>
    <li>Access-Control-Allow-Headers: x-requested-with</li>
  </ul>
  <p>Click a button to see if the page allows CORS</p>
  <form name="form1" action="" method="get">
    <input type="button" name="btn1" value="Check Google Page" onClick="checkCorsSource('google')">
    <input type="button" name="btn1" value="Check Cors Page" onClick="checkCorsSource('cors')">
  </form>
  <p id="txt1" />
</body>
</html>

Victor Stoddard
quelle
1

Ich hatte genau das gleiche Problem, bei dem jquery ajax mir nur Cors-Probleme bei Post-Anfragen gab, bei denen Anfragen gut funktionierten - ich habe alles oben müde gemacht, ohne Ergebnisse. Ich hatte die richtigen Header auf meinem Server usw. Die Umstellung auf XMLHTTPRequest anstelle von jquery hat mein Problem sofort behoben. Egal welche Version von jquery ich verwendet habe, es wurde nicht behoben. Fetch funktioniert auch ohne Probleme, wenn Sie keine Abwärtsbrowser-Kompatibilität benötigen.

        var xhr = new XMLHttpRequest()
        xhr.open('POST', 'https://mywebsite.com', true)
        xhr.withCredentials = true
        xhr.onreadystatechange = function() {
          if (xhr.readyState === 2) {// do something}
        }
        xhr.setRequestHeader('Content-Type', 'application/json')
        xhr.send(json)

Hoffentlich hilft dies allen anderen bei den gleichen Problemen.

Ozzieisaacs
quelle
1

Dies ist eine Zusammenfassung dessen, was für mich funktioniert hat:

Definieren Sie eine neue Funktion ( $.ajaxzur Vereinfachung umbrochen ):

jQuery.postCORS = function(url, data, func) {
  if(func == undefined) func = function(){};
  return $.ajax({
    type: 'POST', 
    url: url, 
    data: data, 
    dataType: 'json', 
    contentType: 'application/x-www-form-urlencoded', 
    xhrFields: { withCredentials: true }, 
    success: function(res) { func(res) }, 
    error: function() { 
            func({}) 
    }
  });
}

Verwendung:

$.postCORS("https://example.com/service.json",{ x : 1 },function(obj){
      if(obj.ok) {
           ...
      }
});

Funktioniert auch mit .done, .failetc:

$.postCORS("https://example.com/service.json",{ x : 1 }).done(function(obj){
      if(obj.ok) {
           ...
      }
}).fail(function(){
    alert("Error!");
});

Legen Sie auf der Serverseite (in diesem Fall, wo example.com gehostet wird) diese Header fest (in PHP wurde ein Beispielcode hinzugefügt):

header('Access-Control-Allow-Origin: https://not-example.com');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 604800');
header("Content-type: application/json");
$array = array("ok" => $_POST["x"]);
echo json_encode($array);

Dies ist der einzige Weg, den ich kenne, um wirklich domänenübergreifend von JS zu POSTEN.

JSONP konvertiert den POST in GET, das möglicherweise vertrauliche Informationen in Serverprotokollen anzeigt.

Lepe
quelle
0

Wenn Sie beim Versuch, Header hinzuzufügen oder Steuerungsrichtlinien festzulegen, aus bestimmten Gründen immer noch nicht weiter kommen, können Sie Apache ProxyPass verwenden.

Fügen Sie beispielsweise in einer <VirtualHost>SSL-Datei die beiden folgenden Anweisungen hinzu:

SSLProxyEngine On
ProxyPass /oauth https://remote.tld/oauth

Stellen Sie sicher, dass die folgenden Apache-Module geladen sind (laden Sie sie mit a2enmod):

  • Proxy
  • proxy_connect
  • proxy_http

Natürlich müssen Sie die URL Ihrer AJAX-Anforderungen ändern, um den Apache-Proxy verwenden zu können.

llange
quelle
-3

Dies ist ein wenig spät für die Party, aber ich habe seit ein paar Tagen damit zu kämpfen. Es ist möglich und keine der Antworten, die ich hier gefunden habe, hat funktioniert. Es ist täuschend einfach. Hier ist der .ajax-Aufruf:

    <!DOCTYPE HTML>
    <html>
    <head>
    <body>
     <title>Javascript Test</title>
     <script src="http://code.jquery.com/jquery-latest.min.js"></script>
     <script type="text/javascript">
     $(document).domain = 'XXX.com';
     $(document).ready(function () {
     $.ajax({
        xhrFields: {cors: false},
        type: "GET",
        url: "http://XXXX.com/test.php?email='[email protected]'",
        success: function (data) {
           alert(data);
        },
        error: function (x, y, z) {
           alert(x.responseText + " :EEE: " + x.status);
        }
    });
    });
    </script> 
    </body>
    </html>

Hier ist das PHP auf der Serverseite:

    <html>
    <head>
     <title>PHP Test</title>
     </head>
    <body>
      <?php
      header('Origin: xxx.com');
      header('Access-Control-Allow-Origin:*');
      $servername = "sqlxxx";
      $username = "xxxx";
      $password = "sss";
      $conn = new mysqli($servername, $username, $password);
      if ($conn->connect_error) {
        die( "Connection failed: " . $conn->connect_error);
      }
      $sql = "SELECT email, status, userdata  FROM msi.usersLive";
      $result = $conn->query($sql);
      if ($result->num_rows > 0) {
      while($row = $result->fetch_assoc()) {
        echo $row["email"] . ":" . $row["status"] . ":" . $row["userdata"] .  "<br>";
      }
    } else {
      echo "{ }";
    }
    $conn->close();
    ?>
    </body>

Steve Johnson
quelle
1
Für das, was es wert ist, ist der Origin-Header ein Anforderungsheader, kein Antwortheader. Ihr PHP-Skript sollte es nicht einstellen.
Nudeln
Hmmph. Dies hat meine Hoffnungen geweckt und sie dann zunichte gemacht, als ich sah, dass Sie ein AJAX "GET" in Ihrem Code erstellen, als das OP ganz klar sagte, dass er versucht hat, "GET" zu vermeiden und "POST" verwenden wollte.
Steve Sauder
CORS-Header funktionieren unabhängig vom verwendeten Verb gleich. Wir können alle Verben über $.ajax()einen korrekt konfigurierten Server aufrufen . Das Access-Control-Request-HeadersSchwierigste ist, das Richtige zu finden, aber selbst das ist nicht allzu schwierig. Wie bereits in früheren Postern erwähnt, darf dies kein Platzhalter sein, sondern eine Whitelist mit Kopfzeilen.
Flucht-llc