web-dev-qa-db-fra.com

Comment puis-je envoyer un inter-domaine POST demande via JavaScript?

Comment envoyer une demande interdomaine POST via JavaScript?

Notes - il ne faut pas rafraîchir la page et je dois ensuite récupérer et analyser la réponse.

530
Ido Schacham

Mise à jour: Avant de continuer, tout le monde devrait lire et comprendre le didacticiel html5rocks on CORS. C'est facile à comprendre et très clair.

Si vous contrôlez le serveur soumis au POST, utilisez simplement la "norme de partage de ressources d'origine croisée" en définissant des en-têtes de réponse sur le serveur. Cette réponse est discutée dans d'autres réponses dans ce fil de discussion, mais pas très clairement à mon avis.

En bref, voici comment vous accomplissez le domaine croisé POST de from.com/1.html à to.com/postHere.php (en utilisant PHP comme exemple). Remarque: vous devez uniquement définir Access-Control-Allow-Origin pour les demandes NON OPTIONS. Cet exemple définit toujours tous les en-têtes pour un extrait de code plus petit.

  1. Dans postHere.php, configurez ce qui suit:

    switch ($_SERVER['HTTP_Origin']) {
        case 'http://from.com': case 'https://from.com':
        header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_Origin']);
        header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
        header('Access-Control-Max-Age: 1000');
        header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
        break;
    }
    

    Cela permet à votre script de créer des POST, GET et OPTIONS entre domaines. Cela deviendra clair à mesure que vous continuerez à lire ...

  2. Configurez votre domaine croisé POST à partir de JS (exemple jQuery):

    $.ajax({
        type: 'POST',
        url: 'https://to.com/postHere.php',
        crossDomain: true,
        data: '{"some":"json"}',
        dataType: 'json',
        success: function(responseData, textStatus, jqXHR) {
            var value = responseData.someKey;
        },
        error: function (responseData, textStatus, errorThrown) {
            alert('POST failed.');
        }
    });
    

Lorsque vous effectuez l'opération POST à l'étape 2, votre navigateur envoie une méthode "OPTIONS" au serveur. Ceci est un "reniflement" par le navigateur pour voir si le serveur est cool avec votre POST. Le serveur répond par un message "Access-Control-Allow-Origin" indiquant au navigateur son consentement à POST | GET | Origin si la requête provient de " http://from.com " ou " https: // from.com ". Puisque le serveur est OK avec, le navigateur fera une deuxième demande (cette fois un POST). Il est judicieux de demander à votre client de définir le type de contenu qu'il envoie - vous devez donc également l'autoriser.

MDN a une excellente description de Contrôle de l'accès HTTP , qui décrit en détail le fonctionnement de l'ensemble du flux. Selon leur documentation, il devrait "fonctionner dans les navigateurs prenant en charge XMLHttpRequest sur plusieurs sites". Ceci est toutefois un peu trompeur, car je THINK seuls les navigateurs modernes autorisent le POST entre domaines. J'ai seulement vérifié que cela fonctionne avec Safari, Chrome, FF 3.6.

Gardez à l’esprit ce qui suit si vous procédez ainsi:

  1. Votre serveur devra gérer 2 requêtes par opération
  2. Vous devrez réfléchir aux implications pour la sécurité. Faites attention avant de faire quelque chose comme 'Access-Control-Allow-Origin: *' 
  3. Cela ne fonctionnera pas sur les navigateurs mobiles. D'après mon expérience, ils n'autorisent pas du tout les domaines POST. J'ai testé Android, iPad, iPhone
  4. Il y a un très gros bogue dans FF <3.6: si le serveur renvoie un code de réponse autre que 400 ET s'il existe un corps de réponse (erreurs de validation par exemple), FF 3.6 n'obtient pas le corps de la réponse. C'est une douleur énorme dans le cul, car vous ne pouvez pas utiliser de bonnes pratiques REST. Voir bug ici (il est classé sous jQuery, mais j’imagine que c’est un bogue FF - semble avoir été corrigé dans FF4).
  5. Renvoyez toujours les en-têtes ci-dessus, pas seulement sur les requêtes OPTION. FF en a besoin dans la réponse du POST.
369
rynop

Si vous contrôlez le serveur distant, vous devriez probablement utiliser CORS, comme décrit dans cette réponse ; Il est pris en charge dans IE8 et versions ultérieures, ainsi que dans toutes les versions récentes de FF, GC et Safari. (Mais dans IE8 et 9, CORS ne vous autorisera pas à envoyer des cookies dans la requête.)

Ainsi, si vous ne contrôlez pas le serveur distant, ou si vous devez prendre en charge IE7, ou si vous avez besoin de cookies et que vous devez prendre en charge IE8/9, vous voudrez probablement utiliser une technique iframe.

  1. Créez un iframe avec un nom unique. (Les iframes utilisent un espace de noms global pour l'ensemble du navigateur. Choisissez donc un nom qu'aucun autre site Web n'utilisera.)
  2. Construisez un formulaire avec des entrées cachées, ciblant l'iframe.
  3. Soumettez le formulaire.

Voici un exemple de code. Je l'ai testé sur IE6, IE7, IE8, IE9, FF4, GC11, S5.

function crossDomainPost() {
  // Add the iframe with a unique name
  var iframe = document.createElement("iframe");
  var uniqueString = "CHANGE_THIS_TO_SOME_UNIQUE_STRING";
  document.body.appendChild(iframe);
  iframe.style.display = "none";
  iframe.contentWindow.name = uniqueString;

  // construct a form with hidden inputs, targeting the iframe
  var form = document.createElement("form");
  form.target = uniqueString;
  form.action = "http://INSERT_YOUR_URL_HERE";
  form.method = "POST";

  // repeat for each parameter
  var input = document.createElement("input");
  input.type = "hidden";
  input.name = "INSERT_YOUR_PARAMETER_NAME_HERE";
  input.value = "INSERT_YOUR_PARAMETER_VALUE_HERE";
  form.appendChild(input);

  document.body.appendChild(form);
  form.submit();
}

Il faut se méfier! Vous ne pourrez pas lire directement la réponse du POST, car l'iframe existe sur un domaine séparé. Les cadres ne sont pas autorisés à communiquer entre eux à partir de différents domaines; c'est la politique même origine .

Si vous contrôlez le serveur distant mais que vous ne pouvez pas utiliser CORS (par exemple parce que vous êtes sur IE8/IE9 et que vous devez utiliser des cookies), il existe des moyens de contourner la stratégie de même origine, par exemple en utilisant window.postMessage et/ou l'une des nombreuses bibliothèques vous permettant d'envoyer des messages inter-domaines entre images dans des navigateurs plus anciens:

Si vous ne contrôlez pas le serveur distant, vous ne pouvez pas lire la réponse du POST, point à la ligne. Sinon, cela causerait des problèmes de sécurité.

118
Dan Fabulich
  1. Créer un iFrame, 
  2. mettre un formulaire dedans avec des entrées cachées, 
  3. définir l'action du formulaire sur l'URL, 
  4. Ajouter iframe au document
  5. soumettre le formulaire

Pseudocode

 var ifr = document.createElement('iframe');
 var frm = document.createElement('form');
 frm.setAttribute("action", "yoururl");
 frm.setAttribute("method", "post");

 // create hidden inputs, add them
 // not shown, but similar (create, setAttribute, appendChild)

 ifr.appendChild(frm);
 document.body.appendChild(ifr);
 frm.submit();

Vous voudrez probablement dénommer l'iframe, être caché et positionné de manière absolue. Je ne suis pas sûr que le navigateur permette l'affichage sur plusieurs sites, mais si c'est le cas, voici comment procéder.

47
Lou Franco

Rester simple: 

  1. POST interdomaine:
    utiliser crossDomain: true,

  2. ne devrait pas rafraîchir la page: 
    Non, la page n'est pas actualisée car le rappel async success ou error est appelé lorsque le serveur renvoie la réponse. 


Exemple de script:

$.ajax({
        type: "POST",
        url: "http://www.yoururl.com/",
        crossDomain: true,
        data: 'param1=value1&param2=value2',
        success: function (data) {
            // do something with server response data
        },
        error: function (err) {
            // handle your error logic here
        }
    });
21
Alain Gauthier

Si vous avez accès à tous les serveurs impliqués, indiquez ce qui suit dans l'en-tête de la réponse pour la page demandée dans l'autre domaine:

PHP:

header('Access-Control-Allow-Origin: *');

Par exemple, dans le code xmlrpc.php de Drupal, vous feriez ceci:

function xmlrpc_server_output($xml) {
    $xml = '<?xml version="1.0"?>'."\n". $xml;
    header('Connection: close');
    header('Content-Length: '. strlen($xml));
    header('Access-Control-Allow-Origin: *');
    header('Content-Type: application/x-www-form-urlencoded');
    header('Date: '. date('r'));
    // $xml = str_replace("\n", " ", $xml); 

    echo $xml;
    exit;
}

Cela crée probablement un problème de sécurité et vous devez vous assurer que vous prenez les mesures appropriées pour vérifier la demande.

16
Robb Lovell

Vérifiez la fonction post_method dans http://taiyolab.com/mbtweet/scripts/twitterapi_call.js - un bon exemple de la méthode iframe décrite ci-dessus.

9
ndeuma
  1. Créez deux iframes cachés (ajoutez "display: none;" au style css). Faites votre deuxième iframe pointer sur quelque chose sur votre propre domaine.

  2. Créez un formulaire masqué, définissez sa méthode sur "post" avec target = votre première iframe et éventuellement enctype sur "multipart/form-data" (je pense que vous souhaitez effectuer POST car vous souhaitez envoyer données en plusieurs parties comme des images?)

  3. Lorsque vous êtes prêt, envoyez le formulaire à submit () le post.

  4. Si vous pouvez demander à l’autre domaine de renvoyer du javascript qui fera la communication interdomaine avec Iframes ( http://softwareas.com/cross-domain-communication-with-iframes ), alors vous avez de la chance peut capturer la réponse aussi bien. 

Bien sûr, si vous voulez utiliser votre serveur comme proxy, vous pouvez éviter tout cela. Soumettez simplement le formulaire sur votre propre serveur, qui enverra la demande par proxy à l'autre serveur (en supposant que l'autre serveur ne soit pas configuré pour remarquer les écarts IP), obtiendra la réponse et renverra ce que vous voudrez.

6
Magarshak

Encore une chose importante à noter !!! Dans exemple ci-dessus, il est décrit comment utiliser 

$.ajax({
    type     : 'POST',
    dataType : 'json', 
    url      : 'another-remote-server',
    ...
});

JQuery 1.6 et versions antérieures ont un bogue avec XHR interdomaine . Selon Firebug, aucune demande à l'exception de OPTIONS n'a été envoyée. Pas d'article. Du tout.

J'ai passé 5 heures à tester/ajuster mon code. Ajout de nombreux en-têtes sur le serveur distant (script). Sans aucun effet . Mais plus tard, j'ai mis à jour JQuery lib à 1.6.4, et tout fonctionne comme un charme.

6
BasTaller

Si vous souhaitez effectuer cette opération dans un environnement ASP.net MVC avec JQuery AJAX, procédez comme suit: (Ceci est un résumé de la solution proposée sur le fil this ). 

Supposons que "caller.com" (quel que soit le site Web) doit publier sur "server.com" (une application ASP.net MVC).

  1. Sur le fichier Web.config de l'application "server.com", ajoutez la section suivante:

      <httpProtocol>
          <customHeaders>
              <add name="Access-Control-Allow-Origin" value="*" />
              <add name="Access-Control-Allow-Headers" value="Content-Type" />
              <add name="Access-Control-Allow-Methods" value="POST, GET, OPTIONS" />
          </customHeaders>
      </httpProtocol>
    
  2. Sur le "server.com", nous aurons l'action suivante sur le contrôleur (appelé "Accueil") sur laquelle nous publierons:

    [HttpPost]
    public JsonResult Save()
    {
        //Handle the post data...
    
        return Json(
            new
            {
                IsSuccess = true
            });
    }
    
  3. Ensuite, depuis "caller.com", envoyez les données d'un formulaire (avec l'ID html "formId") sur "server.com" comme suit:

    $.ajax({
            type: "POST",
            url: "http://www.server.com/home/save",
            dataType: 'json',
            crossDomain: true,
            data: $(formId).serialize(),
            success: function (jsonResult) {
               //do what ever with the reply
            },
            error: function (jqXHR, textStatus) {
                //handle error
            }
        });
    
5
Sujeewa

Je pense que le meilleur moyen est d'utiliser XMLHttpRequest (par exemple, $ .ajax (), $ .post () dans jQuery) avec l'un des polyfills de Cross-Origin Resource Sharing https://github.com/Modernizr/Modernizr/wiki/ HTML5-Cross-Browser-Polyfills # wiki-CORS

3
Roman Dolgiy

C'est une vieille question, mais certaines nouvelles technologies pourraient aider quelqu'un.

Si vous avez un accès administratif à l'autre serveur, vous pouvez utiliser le projet opensource Forge pour réaliser votre POST entre domaines. Forge fournit un wrapper JavaScript XmlHttpRequest inter-domaines qui tire parti de l'API de socket brute de Flash. Le POST peut même être fait sur TLS.

La raison pour laquelle vous avez besoin d'un accès administratif au serveur sur lequel vous postez est due au fait que vous devez fournir une stratégie inter-domaines autorisant l'accès depuis votre domaine.

http://github.com/digitalbazaar/forge

3
dlongley

Niveau élevé .... Vous devez avoir une configuration cname sur votre serveur pour que other-serve.your-server.com pointe sur other-server.com.

Votre page crée dynamiquement un iframe invisible, qui sert de transport à other-server.com. Vous devez ensuite communiquer via JS de votre page vers le serveur other-server.com et demander des rappels qui renvoient les données à votre page. 

Possible mais nécessite une coordination de votre-serveur.com et autre-serveur.com

3
Dan

Il existe un moyen supplémentaire (utilisation de la fonctionnalité html5). Vous pouvez utiliser un proxy iframe hébergé sur cet autre domaine, vous envoyez un message à postFrame via postMessage, puis cette iframe peut faire POST demander (sur le même domaine) et redonner postMessage avec restitution à la fenêtre parent.

parent sur sender.com

var win = $('iframe')[0].contentWindow

function get(event) {
    if (event.Origin === "http://reciver.com") {
        // event.data is response from POST
    }
}

if (window.addEventListener){
    addEventListener("message", get, false)
} else {
    attachEvent("onmessage", get)
}
win.postMessage(JSON.stringify({url: "URL", data: {}}),"http://reciver.com");

iframe sur reciver.com

function listener(event) {
    if (event.Origin === "http://sender.com") {
        var data = JSON.parse(event.data);
        $.post(data.url, data.data, function(reponse) {
            window.parent.postMessage(reponse, "*");
        });
    }
}
// don't know if we can use jQuery here
if (window.addEventListener){
    addEventListener("message", listener, false)
} else {
    attachEvent("onmessage", listener)
}
3
jcubic

Cela devrait être possible avec une table personnalisée YQL + JS XHR, consultez: http://developer.yahoo.com/yql/guide/index.html

Je l'utilise pour faire des travaux côté client (js) html, fonctionne très bien.

1
Guillaume86

Je sais que c'est une vieille question, mais je voulais partager mon approche. J'utilise cURL comme proxy, très facile et cohérent. Créez une page php appelée submit.php, et ajoutez le code suivant:

<?

function post($url, $data) {
$header = array("User-Agent: " . $_SERVER["HTTP_USER_AGENT"], "Content-Type: application/x-www-form-urlencoded");
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}

$url = "your cross domain request here";
$data = $_SERVER["QUERY_STRING"];
echo(post($url, $data));

Ensuite, dans votre js (jQuery here):

$.ajax({
type: 'POST',
url: 'submit.php',
crossDomain: true,
data: '{"some":"json"}',
dataType: 'json',
success: function(responseData, textStatus, jqXHR) {
    var value = responseData.someKey;
},
error: function (responseData, textStatus, errorThrown) {
    alert('POST failed.');
}
});
1
Ivan Durst

Si vous avez accès au serveur interdomaine et ne souhaitez apporter aucune modification de code côté serveur, vous pouvez utiliser une bibliothèque appelée - 'xdomain'. 

Comment ça marche: 

Étape 1: Serveur 1: incluez la bibliothèque xdomain et configurez le domaine croisé en tant qu'esclave:

<script src="js/xdomain.min.js" slave="https://crossdomain_server/proxy.html"></script>

Étape 2: sur le serveur interdomaine, créez un fichier proxy.html et incluez le serveur 1 en tant que maître:

proxy.html:
<!DOCTYPE HTML>
<script src="js/xdomain.min.js"></script>
<script>
  xdomain.masters({
    "https://server1" : '*'
  });
</script>

Étape 3:

Vous pouvez désormais appeler AJAX vers le fichier proxy.html en tant que point de terminaison à partir de server1. C'est contourner la demande CORS. La bibliothèque utilise en interne la solution iframe qui fonctionne avec les informations d'identification et toutes les méthodes possibles: GET, POST, etc.

Requête code ajax:

$.ajax({
        url: 'https://crossdomain_server/proxy.html',
        type: "POST",
        data: JSON.stringify(_data),
        dataType: "json",
        contentType: "application/json; charset=utf-8"
    })
    .done(_success)
    .fail(_failed)
0
Raj Malakpet