Come far funzionare una richiesta post di condivisione delle risorse tra le origini (CORS)


221

Ho una macchina sulla mia lan locale (machineA) che ha due server web. Il primo è quello integrato in XBMC (sulla porta 8080) e mostra la nostra libreria. Il secondo server è uno script Python CherryPy (porta 8081) che sto utilizzando per attivare una conversione di file su richiesta. La conversione del file viene attivata da una richiesta AJAX POST dalla pagina servita dal server XBMC.

  • Vai a http: // machineA: 8080 che visualizza la libreria
  • Viene visualizzata la libreria
  • L'utente fa clic sul collegamento "converti" che emette il seguente comando:

jQuery Ajax Request

$.post('http://machineA:8081', {file_url: 'asfd'}, function(d){console.log(d)})
  • Il browser invia una richiesta HTTP OPTIONS con le seguenti intestazioni;

Intestazione richiesta - OPZIONI

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
  • Il server risponde con quanto segue;

Intestazione risposta - OPZIONI (STATO = 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
  • La conversazione quindi si interrompe. Il browser, in teoria, dovrebbe emettere una richiesta POST poiché il server ha risposto con le intestazioni CORS corrette (?) (Access-Control-Allow-Origin: *)

Per la risoluzione dei problemi, ho anche emesso lo stesso comando $ .post da http://jquery.com . Qui è dove mi trovo perplesso, da jquery.com, la richiesta di post funziona, una richiesta di OPZIONI viene inviata seguita da un POST. Le intestazioni di questa transazione sono di seguito;

Intestazione richiesta - OPZIONI

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

Intestazione risposta - OPZIONI (STATO = 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

Intestazione richiesta - 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

Intestazione risposta - 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

Non riesco a capire perché la stessa richiesta potrebbe funzionare da un sito, ma non dall'altro. Spero che qualcuno possa essere in grado di sottolineare cosa mi manca. Grazie per l'aiuto!


CORS è necessario se entrambi i server Web si trovano sulla stessa macchina?
jdigital

8
Per quanto ne so, si tratta di una richiesta CORS a causa del diverso port. Inoltre, la richiesta OPTIONS indica che il browser la tratta come una richiesta CORS
James

Risposte:


160

Alla fine mi sono imbattuto in questo link " Una richiesta CORS POST funziona da semplice javascript, ma perché non con jQuery? " Che rileva che jQuery 1.5.1 aggiunge il

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

intestazione a tutte le richieste CORS. jQuery 1.5.2 non lo fa. Inoltre, secondo la stessa domanda, l'impostazione di un'intestazione di risposta del server di

Access-Control-Allow-Headers: *

non consente alla risposta di continuare. È necessario assicurarsi che l'intestazione della risposta includa specificamente le intestazioni richieste. cioè:

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

69

RICHIESTA:

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

RISPOSTA:

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

return response

3
se il server non accetta cross origin, crossdomain = true non deve risolvere il problema. l'utilizzo di dataType: "jsonp" e l'impostazione del callback come jsonpCallback: "response" sarebbe l'idea migliore per farlo. Vedi anche: api.jquery.com/jquery.ajax
BonifatiusK

17

Ho risolto il mio problema utilizzando l'API della matrice di distanza di Google impostando la mia intestazione della richiesta con Jquery ajax. dai un'occhiata qui sotto.

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);

      });

Nota cosa ho aggiunto nelle impostazioni
**

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

**
Spero che questo aiuti.


questa è l'unica soluzione che ha funzionato per noi senza cambiare nulla sul lato server ... grazie miracool
Timsta

2
@Timsta, sono felice che il mio suggerimento abbia funzionato per te. Grato anche per lo stack overflow. Vi auguro una buona giornata.
Miracool

13

Mi ci è voluto del tempo per trovare la soluzione.

Nel caso la vostra risposta del server in modo corretto e la richiesta è il problema, si dovrebbe aggiungere withCredentials: truealla xhrFieldsnella richiesta:

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

Nota: jQuery> = 1.5.1 è obbligatorio


versione jquery ok? hai impostato il withCredentials: true? sei sicuro di avere le intestazioni pertinenti?
Dekel

Sì, 1 withCredential: true , jquery version: 3.2.1`. In effetti sta lavorando tramite postino, ma non sta passando attraverso il browser Chrome
Mox Shah

1
Sono quasi sicuro che il postino non dovrebbe avere problemi con CORS perché non è un browser e il suo comportamento è diverso. Sei sicuro di avere le intestazioni corrette e pertinenti inviate dal server al client? Ancora una volta, nota che questa modifica non è sufficiente. È necessario assicurarsi che la risposta del server con le intestazioni corrette .
Dekel

Potresti dirmi quali sono le intestazioni di risposta richieste sul server?
Mox Shah

@MoxShah questa è una domanda completamente diversa e ci sono molte risorse lì :)
Dekel

9

Beh, ho lottato con questo problema per un paio di settimane.

Il modo più semplice, più conforme e non hacker per farlo è probabilmente utilizzare un'API JavaScript del provider che non effettua chiamate basate sul browser e può gestire le richieste Cross Origin.

Ad esempio, l'API JavaScript di Facebook e l'API JS di Google.

Nel caso in cui il tuo provider API non sia aggiornato e non supporti l'intestazione Cross Origin Resource Origin '*' nella sua risposta e non abbia un'API JS (Sì, sto parlando di te Yahoo), sei colpito da una delle tre opzioni:

  1. Utilizzo di jsonp nelle tue richieste che aggiunge una funzione di callback al tuo URL dove puoi gestire la tua risposta. Attenzione, questo cambierà l'URL della richiesta, quindi il tuo server API deve essere attrezzato per gestire il? Callback = alla fine dell'URL.

  2. Invia la richiesta al tuo server API che è il controller da te e si trova nello stesso dominio del client o ha la condivisione delle risorse tra le origini abilitata da cui puoi inoltrare la richiesta al server API di terze parti.

  3. Probabilmente più utile nei casi in cui stai effettuando richieste OAuth e devi gestire l'interazione dell'utente Haha! window.open('url',"newwindowname",'_blank', 'toolbar=0,location=0,menubar=0')


4

L'utilizzo in combinazione con Laravel ha risolto il mio problema. Aggiungi semplicemente questa intestazione alla tua richiesta jquery Access-Control-Request-Headers: x-requested-withe assicurati che la tua risposta lato server abbia questa intestazione impostata Access-Control-Allow-Headers: *.


6
Non c'è motivo per aggiungere manualmente le intestazioni CORS alla richiesta. Il browser aggiungerà sempre le intestazioni CORS prop alla richiesta per te.
Ray Nicholus

1
La vera sfida è far sì che il server risponda con una corretta Access-Control-Allow-Headerse JQ che fornisce il corretto Access-Control-Request-Headers(più qualsiasi cosa che aggiungi tramite codice) nessuno dei quali può essere un carattere jolly. ci vuole solo un'intestazione "cattiva" per far saltare in aria il pre-volo, ad esempio usando If-None-Matchper un GET condizionale, se il server non lo ha elencato.
escape-llc

3

Questo è un riepilogo di ciò che ha funzionato per me:

Definisci una nuova funzione (racchiusa $.ajaxper semplificare):

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

Utilizzo:

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

Funziona anche con .done, .failecc:

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

Lato server (in questo caso in cui è ospitato example.com), imposta queste intestazioni (aggiunto del codice di esempio in PHP):

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);

Questo è l'unico modo che conosco per eseguire il POST interdominio da JS.

JSONP converte il POST in GET che può visualizzare informazioni sensibili nei log del server.


1

Per qualche motivo, una domanda sulle richieste GET è stata fusa con questa, quindi risponderò qui.

Questa semplice funzione otterrà in modo asincrono una risposta di stato HTTP da una pagina abilitata per CORS. Se lo esegui, vedrai che solo una pagina con le intestazioni corrette restituisce uno stato 200 se si accede tramite XMLHttpRequest, indipendentemente dal fatto che venga utilizzato GET o POST. Niente può essere fatto sul lato client per aggirare questo problema, tranne possibilmente usando JSONP se hai solo bisogno di un oggetto json.

Quanto segue può essere facilmente modificato per ottenere i dati contenuti nell'oggetto xmlHttpRequestObject:

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>


1

Ho avuto lo stesso identico problema in cui jquery ajax mi ha dato problemi di cors solo su richieste di post in cui le richieste di ricezione hanno funzionato bene - ho stancato tutto sopra senza risultati. Avevo le intestazioni corrette nel mio server, ecc. Il passaggio all'utilizzo di XMLHTTPRequest invece di jquery ha risolto immediatamente il mio problema. Non importa quale versione di jquery ho usato, non l'ha risolto. Fetch funziona anche senza problemi se non è necessaria la compatibilità del browser con le versioni precedenti.

        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)

Si spera che questo aiuti chiunque altro con gli stessi problemi.


0

Se per alcuni motivi durante il tentativo di aggiungere intestazioni o impostare criteri di controllo non stai ancora arrivando da nessuna parte, potresti prendere in considerazione l'utilizzo di apache ProxyPass ...

Ad esempio in uno <VirtualHost>che utilizza SSL aggiungere le due seguenti direttive:

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

Assicurati che i seguenti moduli apache siano caricati (caricali usando a2enmod):

  • proxy
  • proxy_connect
  • proxy_http

Ovviamente dovrai cambiare l'URL delle tue richieste AJAX per utilizzare il proxy Apache ...


-3

È un po 'tardi per la festa, ma ho lottato con questo per un paio di giorni. È possibile e nessuna delle risposte che ho trovato qui ha funzionato. È apparentemente semplice. Ecco la chiamata .ajax:

    <!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='steve@XXX.com'",
        success: function (data) {
           alert(data);
        },
        error: function (x, y, z) {
           alert(x.responseText + " :EEE: " + x.status);
        }
    });
    });
    </script> 
    </body>
    </html>

Ecco il php sul lato server:

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


1
Per quello che vale, l'intestazione Origin è un'intestazione di richiesta, non un'intestazione di risposta. Il tuo script php non dovrebbe impostarlo.
Noodles

Hmmph. Questo ha acceso le mie speranze e poi le ha deluse quando ho visto che stai facendo un AJAX "GET" nel tuo codice, quando l'OP ha detto chiaramente che stava cercando di EVITARE di usare "GET" e voleva usare "POST".
Steve Sauder

Le intestazioni CORS funzionano allo stesso modo indipendentemente dal verbo coinvolto. Siamo in grado di invocare tutti i verbi tramite $.ajax()su un server configurato correttamente. La parte più difficile è ottenere il Access-Control-Request-Headerscorretto, ma anche questo non è troppo difficile. Come notato dai poster precedenti, questo non deve essere un carattere jolly ma una whitelist di intestazioni.
escape-llc
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.