Come posso inviare una richiesta POST tra domini tramite JavaScript?


568

Come posso inviare una richiesta POST tra domini tramite JavaScript?

Note: non dovrebbe aggiornare la pagina e dopo devo prendere e analizzare la risposta.


Vorrei sapere un po 'del caso d'uso che ti consente di provare a farlo. Potresti dirci qualcosa a riguardo?
mkoeller,

Fondamentalmente sto lavorando a uno script che deve inviare del testo da un file HTML a un altro server per l'elaborazione.
Ido Schacham,

3
Puoi impostare un proxy che faccia questo sul lato server e dia il risultato al tuo script? O deve essere JavaScript al 100%?
Sasha Chedygov,

Risposte:


382

Aggiornamento: prima di continuare, tutti dovrebbero leggere e comprendere il tutorial html5rocks su CORS. È facile da capire e molto chiaro.

Se controlli il server POST, usa semplicemente lo "Standard di condivisione delle risorse tra le origini" impostando le intestazioni di risposta sul server. Questa risposta è discussa in altre risposte in questo thread, ma non molto chiaramente secondo me.

In breve, ecco come realizzare il POST tra domini da da.com/1.html a a.com/postHere.php (usando PHP come esempio). Nota: è necessario impostare solo Access-Control-Allow-Originper OPTIONSrichieste NON : in questo esempio vengono sempre impostate tutte le intestazioni per uno snippet di codice più piccolo.

  1. In postHere.php imposta quanto segue:

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

    Ciò consente al tuo script di creare POST, GET e OPTIONS tra domini. Questo diventerà chiaro mentre continui a leggere ...

  2. Imposta il tuo POST interdominio da JS (esempio 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.');
        }
    });
    

Quando esegui il POST al passaggio 2, il browser invierà un metodo "OPTIONS" al server. Questo è un "annusare" dal browser per vedere se il server è bello con te POST. Il server risponde con un "Access-Control-Allow-Origin" che dice al browser che è OK a POST | GET | ORIGIN se la richiesta proviene da " http://from.com " o " https://from.com ". Poiché il server è OK, il browser farà una seconda richiesta (questa volta un POST). È buona norma avere il client impostato il tipo di contenuto che sta inviando, quindi è necessario consentire anche quello.

MDN ha un ottimo resoconto sul controllo degli accessi HTTP , che spiega in dettaglio come funziona l'intero flusso. Secondo i loro documenti, dovrebbe "funzionare nei browser che supportano XMLHttpRequest tra siti". Questo è un po 'fuorviante, dato che PENSO che solo i browser moderni consentano il POST tra domini. Ho verificato che funziona solo con Safari, Chrome, FF 3.6.

Tieni presente quanto segue se esegui questa operazione:

  1. Il tuo server dovrà gestire 2 richieste per operazione
  2. Dovrai pensare alle implicazioni sulla sicurezza. Fai attenzione prima di fare qualcosa come 'Access-Control-Allow-Origin: *'
  3. Questo non funzionerà sui browser mobili. Nella mia esperienza non consentono affatto il POST tra domini. Ho testato Android, iPad, iPhone
  4. C'è un bug abbastanza grande in FF <3.6 dove se il server restituisce un codice di risposta non 400 E c'è un corpo di risposta (ad esempio errori di validazione), FF 3.6 non otterrà il corpo di risposta. Questo è un grande dolore nel culo, dal momento che non puoi usare buone pratiche REST. Vedi bug qui (è archiviato in jQuery, ma la mia ipotesi è che si tratti di un bug FF - sembra essere stato corretto in FF4).
  5. Restituisci sempre le intestazioni sopra, non solo sulle richieste OPTION. FF ne ha bisogno nella risposta del POST.

Può restituire html per esempio? Devo restituire html e qualcosa non funziona ...
denis_n

Sì, dovresti essere in grado di farlo. Non l'ho mai provato. Il tuo server ha restituito 200? Inoltre il tuo server sta restituendo le intestazioni sulle richieste OPZIONI E POST? Ho aggiornato la mia risposta con maggiori dettagli al riguardo. Assicurati che il tuo server risponda anche con l'intestazione del tipo di contenuto corretta (come text / html). La mia raccomandazione è di utilizzare google chrome, fare clic con il pulsante destro del mouse sulla pagina> ispeziona elemento. Fai clic sulla scheda Rete e guarda il POST e la risposta. Dovrebbe darti informazioni su cosa non va.
rynop

Ho provato questo, ma ottengo ancora 400 Bad Requestsu OPTIONSrichiesta. e nella firefoxseconda richiesta di POSTnon viene mai effettuata. :(
Zain Shaikh,

C'è un modo per richiamare il tuo computer locale nella precedente dichiarazione del caso? Oppure devi solo usare * in questo caso per le origini consentite.
Todd Vance,

1
questa è stata modificata l'ultima volta 4 anni fa - funzionerà ora sui browser mobili?
Frankpinto,

121

Se controlli il server remoto, dovresti probabilmente usare CORS, come descritto in questa risposta ; è supportato in IE8 e versioni successive e in tutte le versioni recenti di FF, GC e Safari. (Ma in IE8 e 9, CORS non ti consentirà di inviare cookie nella richiesta.)

Quindi, se non controlli il server remoto, o se devi supportare IE7, o se hai bisogno di cookie e devi supportare IE8 / 9, probabilmente vorrai usare una tecnica iframe.

  1. Crea un iframe con un nome univoco. (iframe utilizza uno spazio dei nomi globale per l'intero browser, quindi scegli un nome che nessun altro sito Web utilizzerà.)
  2. Costruisci un modulo con input nascosti, mirando all'iframe.
  3. Invia il modulo

Ecco un codice di esempio; L'ho testato su 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();
}

Attenzione! Non sarai in grado di leggere direttamente la risposta del POST, poiché l'iframe esiste su un dominio separato. I frame non sono autorizzati a comunicare tra loro da domini diversi; questa è la stessa politica di origine .

Se controlli il server remoto ma non puoi utilizzare CORS (ad es. Perché sei su IE8 / IE9 e devi usare i cookie), ci sono modi per aggirare la stessa politica di origine, ad esempio usando window.postMessagee / o una delle numerose librerie che ti consentono di inviare messaggi cross-frame tra domini nei browser più vecchi:

Se non controlli il server remoto, non puoi leggere la risposta del POST, punto. Altrimenti causerebbe problemi di sicurezza.


2
Dovrai impostare form.target su qualcosa, altrimenti il ​​browser si sposterà dal tuo sito all'URL dell'azione del modulo. Inoltre, la stringa deve essere unica; se ci sono altri frame o finestre che usano lo stesso nome, il modulo potrebbe pubblicare su quella finestra invece del tuo iframe. Ma quanto deve essere unico? Probabilmente non molto. Le probabilità di ostruzione sono piuttosto piccole. scrollata di spalle
Dan Fabulich,

1
@Nawaz Come ho detto nella mia risposta, dovrai ottenere comunicazioni tra domini tra domini per ottenere il risultato nella tua pagina web. Richiede il controllo del server Web remoto in modo da poter modificare la sua risposta per consentire la comunicazione con la pagina Web. (Per prima cosa, il server dovrà rispondere con HTML; se il server risponde con XML non
elaborato

1
+1: questa è la soluzione migliore che ho trovato se non si ha accesso al server
James Long,

1
@VojtechB No, sarebbe un buco nella sicurezza.
Dan Fabulich,

1
@Andrus Puoi leggere il risultato del POST, ma solo se controlli il server! Vedi in quella risposta, dice "do X sul mittente [client], do Y sul destinatario [server]". Se non controlli il ricevitore / server, non puoi fare Y e quindi non puoi leggere il risultato del POST.
Dan Fabulich,

48
  1. Crea un iFrame,
  2. inserire un modulo con input nascosti,
  3. imposta l'azione del modulo sull'URL,
  4. Aggiungi iframe al documento
  5. invia il modulo

pseudocodice

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

Probabilmente vuoi dare uno stile all'iframe, essere nascosto e assolutamente posizionato. Non sono sicuri che il browser consentirà la pubblicazione su più siti, ma in tal caso, ecco come farlo.


4
In realtà, questo è leggermente inaccurato, poiché ifr.appendChild (frm); non funzionerà. iframe è un riferimento a un oggetto finestra e il metodo appendChild non esiste per esso. Dovrai prima prendere il nodo del documento nell'iframe. Ciò richiede il rilevamento delle funzionalità per funzionare su tutti i browser.
Rakesh Pai,


19
Problema! La risposta ricevuta nell'iframe si trova in un dominio diverso, quindi la finestra principale non ha accesso ad essa, né l'iframe ha accesso alla finestra principale. Quindi questa soluzione sembra buona solo per fare il POST, ma non puoi analizzare la risposta in seguito :(
Ido Schacham,

2
Prova a impostare un onload nel tag body della risposta a una funzione JavaScript che chiama una funzione nel genitore con la stringa di risposta.
Lou Franco,

Questa risposta non ha funzionato per me; Ho pubblicato la mia variante di seguito.
Dan Fabulich,

24

Mantienilo semplice:

  1. POST tra domini:
    utilizzarecrossDomain: true,

  2. non dovrebbe aggiornare la pagina:
    No, non si aggiorna la pagina comesuccessoerrorasincrono callback verrà chiamata quando il server di invio di nuovo la risposta.


Script di esempio:

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

8
crossDomain: truestranamente non ha assolutamente nulla a che fare con le richieste reali tra domini. Se la richiesta è tra domini, jquery lo imposta su true automagicamente.
Kevin B,

16

Se hai accesso a tutti i server coinvolti, inserisci quanto segue nell'intestazione della risposta per la pagina richiesta nell'altro dominio:

PHP:

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

Ad esempio, nel codice xmlrpc.php di Drupal faresti questo:

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

Questo probabilmente crea un problema di sicurezza e dovresti assicurarti di prendere le misure appropriate per verificare la richiesta.



6
  1. Crea due iframe nascosti (aggiungi "display: none;" allo stile css). Fai in modo che il tuo secondo iframe punti a qualcosa sul tuo dominio.

  2. Crea un modulo nascosto, imposta il suo metodo su "pubblica" con target = il tuo primo iframe e, facoltativamente, imposta il tipo su "multipart / form-data" (Sto pensando che vuoi fare POST perché vuoi inviare dati multipart come immagini ?)

  3. Quando sei pronto, invia il modulo submit () il POST.

  4. Se riesci a ottenere l'altro dominio per restituire javascript che farà comunicazione tra domini con Iframe ( http://softwareas.com/cross-domain-communication-with-iframes ), allora sei fortunato e puoi catturare la risposta anche.

Naturalmente, se si desidera utilizzare il server come proxy, è possibile evitare tutto ciò. Invia semplicemente il modulo al tuo server, che eseguirà il proxy della richiesta all'altro server (supponendo che l'altro server non sia impostato per rilevare discrepanze IP), ottieni la risposta e restituisci quello che preferisci.


6

Un'altra cosa importante da notare !!! Nel esempio sopra è descritto come utilizzare

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

JQuery 1.6 e precedenti hanno un bug con XHR tra domini. Secondo Firebug non sono state inviate richieste tranne OPTIONS. Nessun post. Affatto.

Ho trascorso 5 ore testando / ottimizzando il mio codice. Aggiunta di molte intestazioni sul server remoto (script). Senza alcun effetto. Ma più tardi, ho aggiornato JQuery lib alla 1.6.4 e tutto funziona come un fascino.


Whoopps, non in Opera 10.61. La mia decisione finale è stata quella di utilizzare il proxy PHP sul mio dominio.
BasTaller,

Come hai usato il proxy PHP? Puoi guidarmi su questo?
Zoran777,

vedere le risposte di seguito, ad esempio di Ivan Durst
BasTaller,

5

Se si desidera eseguire questa operazione nell'ambiente ASP.net MVC con JQuery AJAX, attenersi alla seguente procedura: (questo è un riepilogo della soluzione offerta in questo thread)

Supponiamo che "caller.com" (può essere qualsiasi sito Web) debba pubblicare su "server.com" (un'applicazione ASP.net MVC)

  1. Sul Web.config dell'app "server.com" aggiungi la seguente sezione:

      <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. Su "server.com" avremo la seguente azione sul controller (chiamata "Home") su cui pubblicheremo:

    [HttpPost]
    public JsonResult Save()
    {
        //Handle the post data...
    
        return Json(
            new
            {
                IsSuccess = true
            });
    }
  3. Quindi da "caller.com", pubblica i dati da un modulo (con l'id html "formId") su "server.com" come segue:

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

4

C'è un altro modo (usando la funzione html5). È possibile utilizzare iframe proxy ospitato su quell'altro dominio, inviare un messaggio utilizzando postMessage a tale iframe, quindi quell'iframe può eseguire la richiesta POST (sullo stesso dominio) e postMessage di nuovo con reposnse nella finestra padre.

genitore su mittente.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 su 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)
}

C'è una domanda correlata in stackoverflow.com/questions/38940932/… . È possibile creare alcuni plugin o funzioni generiche in base al tuo esempio?
Andrus,


Questo codice richiede la modifica della pagina del destinatario. Come leggere la risposta se le pagine del destinatario non possono essere modificate?
Andrus,

@Andrus non puoi avere bisogno di avere accesso a iframe recever.com per inviare richieste ajax lì. Senza iframe non ci saranno richieste.
jcubic,

3

Alto livello .... Devi avere una configurazione cname sul tuo server in modo che other-serve.your-server.com punti ad other-server.com.

La tua pagina crea dinamicamente un iframe invisibile, che funge da trasporto verso other-server.com. Devi quindi comunicare via JS dalla tua pagina ad other-server.com e avere richiamate che restituiscono i dati alla tua pagina.

Possibile ma richiede il coordinamento da your-server.com e other-server.com


Non ho nemmeno pensato di usare un CNAME per reindirizzare. Ottima scelta! Devo ancora provare questo, ma presumo che il CNAME indurrà il browser a pensare che stia interagendo con lo stesso sito? Lo userò per pubblicare su Amazon S3, quindi spero che funzioni.
SpencerElliott

1
Non vedo come questo risolva qualsiasi cosa. il passaggio a un sottodominio diverso ha gli stessi problemi del passaggio a un dominio diverso.
Polpo,


2

Questa è una vecchia domanda, ma alcune nuove tecnologie potrebbero aiutare qualcuno.

Se si dispone dell'accesso amministrativo all'altro server, è possibile utilizzare il progetto Openource Forge per realizzare il POST tra domini. Forge fornisce un wrapper XmlHttpRequest JavaScript tra domini che sfrutta l'API raw socket di Flash. Il POST può anche essere eseguito su TLS.

Il motivo per cui hai bisogno dell'accesso amministrativo al server a cui stai POSTANDO è perché devi fornire una politica tra domini che consenta l'accesso dal tuo dominio.

http://github.com/digitalbazaar/forge


2

So che questa è una vecchia domanda, ma volevo condividere il mio approccio. Uso cURL come proxy, molto semplice e coerente. Crea una pagina php chiamata submit.php e aggiungi il seguente codice:

<?

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

Quindi, nel tuo js (jQuery qui):

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

Dovrebbe essere possibile con una tabella personalizzata YQL + JS XHR, dai un'occhiata a: http://developer.yahoo.com/yql/guide/index.html

Lo uso per eseguire alcuni scraping html lato client (js), funziona bene (ho un lettore audio completo, con ricerca su internet / playlist / testi / informazioni fm ultime, tutti i client js + YQL)


1

CORS è per te. CORS è "Condivisione delle risorse tra origini", è un modo per inviare richieste tra domini. Ora XMLHttpRequest2 e l'API Fetch supportano entrambi CORS e possono inviare sia richieste POST che GET

Ma ha i suoi limiti. Il server deve rivendicare in modo specifico Access-Control-Allow-Origin e non può essere impostato su '*'.

E se vuoi che qualsiasi origine possa inviarti una richiesta, hai bisogno di JSONP (devi anche impostare Access-Control-Allow-Origin , ma può essere '*')

Per molte modalità di richiesta se non sai come scegliere, penso che tu abbia bisogno di un componente completamente funzionale per farlo. Permettimi di presentare un semplice componente https://github.com/Joker-Jelly/catta


Se stai utilizzando un browser moderno (> IE9, Chrome, FF, Edge, ecc.), Consiglio vivamente di utilizzare un componente semplice ma di bellezza https://github.com/Joker-Jelly/catta . Non ha dipendenza, Meno di 3 KB e supporta Fetch, AJAX e JSONP con la stessa sintassi e opzioni micidiali.

catta('./data/simple.json').then(function (res) {
  console.log(res);
});

Supporta inoltre l'importazione nel tuo progetto, come il modulo ES6, CommonJS e persino <script>in HTML.


1

Se hai accesso al server interdominio e non desideri apportare modifiche al codice sul lato server, puoi utilizzare una libreria chiamata "xdomain".

Come funziona:

Passaggio 1: server 1: includere la libreria xdomain e configurare il dominio incrociato come slave:

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

Passaggio 2: sul server interdominio, creare un file proxy.html e includere il server 1 come master:

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

Passaggio 3:

Ora è possibile effettuare una chiamata AJAX a proxy.html come endpoint dal server1. Questo è bypassare la richiesta CORS. La libreria utilizza internamente una soluzione iframe che funziona con credenziali e tutti i metodi possibili: GET, POST ecc.

Richiesta codice 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)
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.