Pubblica dati su JsonP


102

È possibile inviare dati a JsonP? O tutti i dati devono essere passati nella stringa di query come richiesta GET?

Ho molti dati che devo inviare al servizio, interdominio, ed è troppo grande per essere inviato tramite la stringa di query

Quali sono le opzioni per aggirare questo problema?

Risposte:


83

Non è possibile eseguire un'operazione asincrona POSTa un servizio su un altro dominio, a causa della limitazione (abbastanza sensata) della stessa policy di origine . JSON-P funziona solo perché ti è consentito inserire<script> tag nel DOM e possono puntare ovunque.

Ovviamente puoi fare di una pagina su un altro dominio l'azione di un normale modulo POST.

Modifica : ci sono alcuni hack interessanti là fuori se sei disposto a fare un grande sforzo per inserire <iframe>s nascosti e perdere tempo con le loro proprietà.


Hai detto che un "POST asincrono" non è possibile .... allora posso fare un POST sincrono?
Marco

4
@mark "synchronous POST" significa inviare un modulo che utilizza <form method = "post" action = "http: // ... / ...">
Steven Kryskalla

8
Questo non è del tutto vero. Certamente puoi fare POSTrichieste ad altri domini purché sia ​​quel dominio che il tuo browser supportino CORS. Ma è del tutto vero POSTe JSONPnon sono compatibili.
Hippietrail

2
JSONP viene implementato inserendo <script>tag che puntano a un altro dominio. L'unico modo per eseguire le richieste POST in un browser è tramite moduli HTML o XMLHttpRequest.
friedo

1
(in generale -) È (!) possibile eseguire un POST asincrono a un servizio su un altro dominio. la limitazione è sulla risposta. la limitazione è anche sulla richiesta JSONP.
Royi Namir

20

Se devi inviare molti dati tra domini. Di solito creo un servizio che puoi chiamare in due passaggi:

  1. Per prima cosa il cliente invia un FORM (post consentito tra domini). Il servizio memorizza l'input nella sessione sul server (utilizzando il GUID come chiave). (il client crea un GUID e lo invia come parte dell'input)

  2. Quindi il client esegue un normale script-inject (JSONP) come parametro che utilizzi lo stesso GUID utilizzato nel post FORM. Il servizio elabora l'input dalla sessione e restituisce i dati nel normale modo JSONP. Dopo questo la sessione viene distrutta.

Questo ovviamente si basa sulla scrittura del server backend.


1
Ho provato il tuo approccio. Ha funzionato per FF14 e Chrome20. Opera11 e IE9 semplicemente non hanno trasferito il post. (L'ho controllato con i loro strumenti di debug e ascoltato sul server dall'altra parte) Forse correlato alla disabilità di IE è questa domanda: stackoverflow.com/questions/10395803/… Chrome reclamo nella console, ma ha fatto ancora il POST: XMLHttpRequest non può load localhost: 8080 / xxx Origin null non è consentito da Access-Control-Allow-Origin.
OneWorld

@ OneWorld - Non hai fatto quello che diceva la risposta. XMLHttpRequestnon dovrebbe essere coinvolto affatto. La risposta di Per utilizza un normale invio di moduli per effettuare la richiesta POST, quindi un'iniezione di elementi di script per effettuare la richiesta GET.
Quentin

7

So che questa è una grave negromanzia, ma ho pensato di pubblicare la mia implementazione di JSONP POST utilizzando jQuery, che sto utilizzando con successo per il mio widget JS (utilizzato per la registrazione e il login del cliente):

Fondamentalmente, sto usando un approccio IFrame, come suggerito nella risposta accettata. Quello che sto facendo in modo diverso è dopo aver inviato la richiesta, sto guardando, se il form può essere raggiunto nell'iframe, utilizzando un timer. Quando il modulo non può essere raggiunto, significa che la richiesta è stata restituita. Quindi, sto usando una normale richiesta JSONP per interrogare lo stato dell'operazione.

Spero che qualcuno lo trovi utile. Testato in> = IE8, Chrome, FireFox e Safari.

function JSONPPostForm(form, postUrl, queryStatusUrl, queryStatusSuccessFunc, queryStatusData)
{
    var tmpDiv = $('<div style="display: none;"></div>');
    form.parent().append(tmpDiv);
    var clonedForm = cloneForm(form);
    var iframe = createIFrameWithContent(tmpDiv, clonedForm);

    if (postUrl)
        clonedForm.attr('action', postUrl);

    var postToken = 'JSONPPOST_' + (new Date).getTime();
    clonedForm.attr('id', postToken);
    clonedForm.append('<input name="JSONPPOSTToken" value="'+postToken+'">');
    clonedForm.attr('id', postToken );
    clonedForm.submit();

    var timerId;
    var watchIFrameRedirectHelper = function()
    {
        if (watchIFrameRedirect(iframe, postToken ))
        {
            clearInterval(timerId);
            tmpDiv.remove();
            $.ajax({
                url:  queryStatusUrl,
                data: queryStatusData,
                dataType: "jsonp",
                type: "GET",
                success: queryStatusSuccessFunc
            });
        }
    }

    if (queryStatusUrl && queryStatusSuccessFunc)
        timerId = setInterval(watchIFrameRedirectHelper, 200);
}

function createIFrameWithContent(parent, content)
{
    var iframe = $('<iframe></iframe>');
    parent.append(iframe);

    if (!iframe.contents().find('body').length)
    {
        //For certain IE versions that do not create document content...
        var doc = iframe.contents().get()[0];
        doc.open();
        doc.close();
    }

    iframe.contents().find('body').append(content);
    return iframe;
}

function watchIFrameRedirect(iframe, formId)
{
    try
    {
        if (iframe.contents().find('form[id="' + formId + '"]').length)
            return false;
        else
            return true;
    }
    catch (err)
    {
        return true;
    }
    return false;
}

//This one clones only form, without other HTML markup
function cloneForm(form)
{
    var clonedForm = $('<form></form>');
    //Copy form attributes
    $.each(form.get()[0].attributes, function(i, attr)
    {
        clonedForm.attr(attr.name, attr.value);
    });
    form.find('input, select, textarea').each(function()
    {
        clonedForm.append($(this).clone());
    });

    return clonedForm;
}

4

In genere JSONP viene implementato aggiungendo un <script>tag al documento chiamante, in modo tale che l'URL del servizio JSONP sia "src". Il browser recupera l'origine dello script con una transazione HTTP GET.

Ora, se il tuo servizio JSONP è nello stesso dominio della tua pagina di chiamata, potresti probabilmente mettere insieme qualcosa con una semplice $.ajax()chiamata. Se non è nello stesso dominio, non sono sicuro di come sarebbe possibile.


In questo caso non è nello stesso dominio. E presumo che sia possibile solo GET, ma volevo controllare perché ho iniziato a leggere solo oggi su JsonP e devo prendere alcune decisioni
sull'idoneità

2
Se non è nello stesso dominio ma supporta CORS, sarà possibile fintanto che lo supporta anche il browser. In questi casi utilizzerai plain JSONanziché JSONP.
Hippietrail

Sì, @hippietrail 2 anni fa una grande differenza :-) CORS lo rende sicuramente possibile, ma ovviamente richiede che l'origine dati sia configurata in modo appropriato.
Pointy

0

È possibile utilizzare un proxy CORS utilizzando questo progetto . Indirizzerebbe tutto il traffico a un endpoint del tuo dominio e inoltrerebbe tali informazioni a un dominio esterno. Poiché il browser registra tutte le richieste sullo stesso dominio, siamo in grado di pubblicare JSON. NOTA: funziona anche con i certificati SSL conservati sul server.


-1

C'è una soluzione (hack) che ho fatto molte volte, sarai in grado di pubblicare con JsonP. (Sarai in grado di inviare un modulo, più grande di 2000 caratteri di quanto puoi usare per GET)

Applicazione client Javascript

$.ajax({
  type: "POST", // you request will be a post request
  data: postData, // javascript object with all my params
  url: COMAPIURL, // my backoffice comunication api url
  dataType: "jsonp", // datatype can be json or jsonp
  success: function(result){
    console.dir(result);
  }
});

GIAVA:

response.addHeader( "Access-Control-Allow-Origin", "*" ); // open your api to any client 
response.addHeader( "Access-Control-Allow-Methods", "POST" ); // a allow post
response.addHeader( "Access-Control-Max-Age", "1000" ); // time from request to response before timeout

PHP:

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Max-Age: 1000');

In questo modo, stai aprendo il tuo server a qualsiasi richiesta di post, dovresti proteggerlo nuovamente fornendo identità o qualcos'altro.

Con questo metodo, puoi anche cambiare il tipo di richiesta da jsonp a json, entrambi funzionano, basta impostare il giusto tipo di contenuto della risposta

jsonp

response.setContentType( "text/javascript; charset=utf-8" );

jSON

response.setContentType( "application/json; charset=utf-8" );

Per favore, non che il tuo server non rispetterà più la SOP (stessa politica di origine), ma chi se ne frega?


Questo non è AJAX con CORS. AJAX implica che stai usando XML. Questo è JSON [P] con CORS. JSONP è "JSON" con "Padding". Se sta inviando dati JSON, racchiusi in una chiamata di funzione per il riempimento, allora è JSONP con CORS. Puoi utilizzare sia notazioni di dati JSON che JSONP al di fuori della semplice iniezione di <script>tag nel tuo DOM HTML (diamine puoi persino usarli in un'app desktop, diciamo che volevi effettuare più richieste JSON allo stesso server e volevi usare il nome della funzione come ID di tracciamento della richiesta, ad esempio).
BrainSlugs83

-6

È possibile, ecco la mia soluzione:

Nel tuo javascript:

jQuery.post("url.php",data).complete(function(data) {
    eval(data.responseText.trim()); 
});
function handleRequest(data){
    ....
}

Nel tuo url.php:

echo "handleRequest(".$responseData.")";

11
In questo caso, molto probabilmente jQuery ha trasformato la tua richiesta in Get in base alla loro documentazione: Nota: questo trasformerà i POST in GET per le richieste di dominio remoto. api.jquery.com/jQuery.ajax
OneWorld
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.