web-dev-qa-db-fra.com

Comment faire fonctionner une demande de publication CORS (Cross-Origin Resource Sharing)

J'ai une machine sur mon réseau local (machineA) qui a deux serveurs Web. Le premier est celui intégré à XBMC (sur le port 8080) et affiche notre bibliothèque. Le second serveur est un script CherryPy python (port 8081) que j'utilise pour déclencher une conversion de fichier à la demande. La conversion de fichier est déclenchée par une demande AJAX POST de la page servie par le serveur XBMC.

  • Goto http: // machineA: 808 qui affiche la bibliothèque
  • La bibliothèque est affichée
  • L'utilisateur clique sur le lien 'convertir' qui lance la commande suivante -

demande jQuery Ajax

$.post('http://machineA:8081', {file_url: 'asfd'}, function(d){console.log(d)})
  • Le navigateur émet une requête HTTP OPTIONS avec les en-têtes suivants;

En-tête de demande - OPTIONS

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
  • Le serveur répond avec ce qui suit:

En-tête de réponse - OPTIONS (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
  • La conversation s'arrête alors. En théorie, le navigateur doit émettre une demande POST car le serveur répond avec les en-têtes CORS corrects (?) (Access-Control-Allow-Origin: *).

Pour le dépannage, j'ai également émis la même commande $ .post de http://jquery.com . C’est là que je suis perplexe: à partir de jquery.com, la demande de publication fonctionne, une demande OPTIONS est envoyée à la suite d’un POST. Les en-têtes de cette transaction sont ci-dessous;

En-tête de demande - OPTIONS

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

En-tête de réponse - OPTIONS (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

En-tête de demande - 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

En-tête de réponse - 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

Je ne peux pas comprendre pourquoi la même demande fonctionnerait sur un site mais pas sur l'autre. J'espère que quelqu'un pourra peut-être signaler ce qui me manque. Merci de votre aide!

195
James

Je suis finalement tombé sur ce lien " ne requête CORS POST fonctionne à partir de javascript simple, mais pourquoi pas avec jQuery? " qui note que jQuery 1.5.1 ajoute le

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

en-tête de toutes les demandes CORS. jQuery 1.5.2 ne fait pas cela. En outre, selon la même question, définir un en-tête de réponse du serveur de

Access-Control-Allow-Headers: *

ne permet pas à la réponse de continuer. Vous devez vous assurer que l'en-tête de réponse inclut spécifiquement les en-têtes requis. c'est à dire:

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

DEMANDE:

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

RÉPONSE:

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

return response
63
Hassan Zaheer

Il m'a fallu du temps pour trouver la solution.

Si votre serveur répond correctement et que la requête pose problème, vous devez ajouter withCredentials: true à la variable xhrFields de la requête:

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

Remarque: jQuery> = 1.5.1 est requis

12
Dekel

Eh bien, je me suis battu avec ce problème pendant quelques semaines.

Pour ce faire, la méthode la plus simple, la plus conforme et la plus simple consiste à utiliser probablement une API JavaScript de fournisseur qui ne fait pas d'appels via un navigateur et qui peut gérer les demandes d'origine croisée.

Par exemple. API Facebook Facebook et API Google JS.

Si votre fournisseur d'API n'est pas à jour et ne prend pas en charge l'en-tête Cross Origin Resource Origin '*' et n'a pas d'API JS (oui, je parle de vous Yahoo), vous êtes frappé par l'une des trois options suivantes:

  1. Utilisation de jsonp dans vos demandes, qui ajoute une fonction de rappel à votre URL où vous pouvez gérer votre réponse. Attention, cela modifiera l'URL de la requête. Votre serveur d'API doit donc être équipé pour gérer le? Callback = à la fin de l'URL.

  2. Envoyez la demande à votre serveur d'API qui est votre contrôleur et qui se trouve soit dans le même domaine que le client, soit sur lequel le partage de ressources d'origine multiple est activé, à partir duquel vous pouvez envoyer la demande par proxy au serveur API tiers.

  3. Probablement plus utile dans les cas où vous faites OAuth demandes et que vous devez gérer l’interaction de l’utilisateur Haha! window.open('url',"newwindowname",'_blank', 'toolbar=0,location=0,menubar=0')

9
Pritam Roy

J'ai résolu mon propre problème lors de l'utilisation de l'API google distance matrix en définissant l'en-tête de ma demande avec Jquery ajax. jetez un oeil ci-dessous.

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

      });

Notez ce que j'ai ajouté aux paramètres
**

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

**
J'espère que ça aide.

4
Miracool

Utiliser ceci en combinaison avec Laravel a résolu mon problème. Ajoutez simplement cet en-tête à votre demande jQuery Access-Control-Request-Headers: x-requested-with et assurez-vous que votre réponse côté serveur a cet en-tête défini Access-Control-Allow-Headers: *.

4
M K

Pour une raison quelconque, une question sur les demandes GET a été fusionnée avec celle-ci, je vais donc y répondre ici.

Cette fonction simple obtiendra de manière asynchrone une réponse de statut HTTP depuis une page activée par CORS. Si vous l'exécutez, vous verrez que seule une page avec les en-têtes appropriés renvoie un statut 200 si elle est accédée via XMLHttpRequest - que GET ou POST soit utilisé. Rien ne peut être fait du côté client pour contourner ce problème, sauf éventuellement utiliser JSONP si vous avez juste besoin d'un objet JSON.

Les éléments suivants peuvent être facilement modifiés pour obtenir les données contenues dans l'objet 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
Victor Stoddard

J'ai eu exactement le même problème où jquery ajax ne m'avait donné que des problèmes concernant les demandes de poste où obtenir des demandes fonctionnaient bien - j'ai fatigué tout ce qui précède sans résultat. J'avais les bons en-têtes sur mon serveur, etc. Passer de XMLHTTPRequest à la place de jquery a immédiatement résolu mon problème. Peu importe la version de jQuery que j'ai utilisée, cela ne résout pas le problème. Fetch fonctionne également sans problème si vous n'avez pas besoin de la compatibilité du navigateur en amont.

        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)

J'espère que cela aidera tout le monde avec les mêmes problèmes.

1
ozzieisaacs

Ceci est un résumé de ce qui a fonctionné pour moi:

Définir une nouvelle fonction (_ enveloppé $.ajax pour simplifier):

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

Usage:

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

Fonctionne également avec .done, .fail, etc:

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

Côté serveur (dans le cas où example.com est hébergé), définissez ces en-têtes (avec quelques exemples de code 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);

C’est le seul moyen que je connaisse pour vraiment POST inter-domaines de JS.

JSONP convertit le POST en GET, qui peut afficher des informations sensibles dans les journaux du serveur.

1
lepe

Si pour certaines raisons, lorsque vous essayez d'ajouter des en-têtes ou de définir une stratégie de contrôle, vous n'obtenez toujours rien, vous pouvez envisager d'utiliser Apache ProxyPass…

Par exemple, dans un <VirtualHost> qui utilise SSL, ajoutez les deux directives suivantes:

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

Assurez-vous que les modules Apache suivants sont chargés (chargez-les à l'aide de a2enmod):

  • procuration
  • proxy_connect
  • proxy_http

Évidemment, vous devrez changer votre URL de requêtes AJAX pour pouvoir utiliser le proxy Apache…

0
llange