Daten an JsonP senden

102

Ist es möglich, Daten an JsonP zu senden? Oder müssen alle Daten als GET-Anfrage im Querystring übergeben werden?

Ich habe viele Daten, die ich domänenübergreifend an den Dienst senden muss, und sie sind zu groß, um über den Querystring gesendet zu werden

Welche Möglichkeiten gibt es, um dies zu umgehen?

ChrisCa
quelle

Antworten:

83

POSTAufgrund der (durchaus sinnvollen) Einschränkung derselben Ursprungsrichtlinie ist es nicht möglich, eine asynchrone Verbindung zu einem Dienst in einer anderen Domäne herzustellen . JSON-P funktioniert nur, weil Sie <script>Tags in das DOM einfügen dürfen und sie überall hin zeigen können.

Sie können natürlich eine Seite in einer anderen Domain zur Aktion eines regulären POST-Formulars machen.

Bearbeiten : Es gibt einige interessante Hacks da draußen, wenn Sie bereit sind, sich viel Mühe zu geben, versteckte <iframe>s einzufügen und mit ihren Eigenschaften herumzuspielen.

Friedo
quelle
Sie haben erwähnt, dass ein "asynchroner POST" nicht möglich ist. Kann ich dann einen synchronen POST durchführen?
Mark
4
@mark "synchroner POST" bedeutet das Senden eines Formulars, das <form method = "post" action = "http: // ... / ..."> verwendet
Steven Kryskalla
8
Das ist nicht ganz richtig. Sie können sicherlich POSTAnfragen an andere Domains stellen, solange diese Domain und Ihr Browser dies unterstützen CORS. Aber es ist völlig richtig, dass POSTund JSONPnicht kompatibel sind.
Hippietrail
2
JSONP wird implementiert, indem <script>Tags eingefügt werden, die auf eine andere Domäne verweisen. Die einzige Möglichkeit, POST-Anforderungen in einem Browser auszuführen, besteht in HTML-Formularen oder XMLHttpRequest.
Friedo
1
(im Allgemeinen -) Es ist (!) möglich, einen asynchronen POST für einen Dienst in einer anderen Domäne durchzuführen. Die Einschränkung liegt in der Antwort. Die Einschränkung gilt auch für JSONP-Anforderungen.
Royi Namir
20

Wenn Sie viele Daten domänenübergreifend senden müssen. Normalerweise erstelle ich einen Dienst, den Sie in zwei Schritten aufrufen können:

  1. Zuerst sendet der Client ein FORMULAR (Post erlaubt domänenübergreifend). Der Dienst speichert die Eingabe in der Sitzung auf dem Server (unter Verwendung der GUID als Schlüssel). (Der Client erstellt eine GUID und sendet sie als Teil der Eingabe.)

  2. Anschließend führt der Client eine normale Skriptinjektion (JSONP) als Parameter durch. Sie verwenden dieselbe GUID wie im FORM-Beitrag. Der Dienst verarbeitet die Eingaben aus der Sitzung und gibt die Daten auf normale JSONP-Weise zurück. Danach wird die Sitzung zerstört.

Dies hängt natürlich davon ab, dass Sie das Server-Backend schreiben.

Pro
quelle
1
Versuchte Ihren Ansatz. Arbeitete für FF14 und Chrome20. Opera11 und IE9 haben den Beitrag einfach nicht übertragen. (Überprüfte es mit ihren Debug-Tools und hörte auf dem Server am anderen Ende zu.) Möglicherweise hängt diese Frage mit der Behinderung des IE zusammen: stackoverflow.com/questions/10395803/… Chrome-Beschwerde in der Konsole, aber POST: XMLHttpRequest kann dies nicht load localhost: 8080 / xxx Origin null ist von Access-Control-Allow-Origin nicht zulässig.
OneWorld
@ OneWorld - Du hast nicht getan, was die Antwort sagte. XMLHttpRequestsollte überhaupt nicht beteiligt sein. Per's Antwort verwendet eine reguläre Formularübermittlung, um die POST-Anfrage zu stellen, und dann eine Skriptelementinjektion, um die GET-Anfrage zu stellen.
Quentin
7

Ich weiß, dass dies eine ernsthafte Nekromantie ist, aber ich dachte, ich würde meine Implementierung von JSONP POST mit jQuery veröffentlichen, das ich erfolgreich für mein JS-Widget verwende (dies wird für die Kundenregistrierung und -anmeldung verwendet):

Grundsätzlich verwende ich einen IFrame-Ansatz, wie in der akzeptierten Antwort vorgeschlagen. Was ich anders mache, ist nach dem Senden der Anfrage, ob das Formular im Iframe mit einem Timer erreichbar ist. Wenn das Formular nicht erreicht werden kann, bedeutet dies, dass die Anforderung zurückgegeben wurde. Dann verwende ich eine normale JSONP-Anforderung, um den Status des Vorgangs abzufragen.

Ich hoffe, dass jemand es nützlich findet. Getestet in> = IE8, Chrome, FireFox und 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;
}
WB
quelle
4

Im Allgemeinen wird JSONP implementiert, indem <script>dem aufrufenden Dokument ein Tag hinzugefügt wird , sodass die URL des JSONP-Dienstes "src" ist. Der Browser ruft die Skriptquelle mit einer HTTP-GET-Transaktion ab.

Befindet sich Ihr JSONP-Dienst in derselben Domäne wie Ihre aufrufende Seite, können Sie wahrscheinlich mit einem einfachen $.ajax()Aufruf etwas zusammenschustern . Wenn es nicht in derselben Domäne ist, bin ich mir nicht sicher, wie es möglich wäre.

Spitze
quelle
In diesem Fall befindet es sich nicht in derselben Domäne. Und ich gehe davon aus, dass nur GET möglich ist, wollte aber überprüfen, da ich erst heute angefangen habe, über JsonP zu lesen, und einige Entscheidungen darüber treffen muss, ob es für das geeignet ist, was ich brauche
ChrisCa
2
Wenn es sich nicht in derselben Domain befindet, aber unterstützt CORSwird, ist dies möglich, solange der Browser dies ebenfalls unterstützt. In diesen Fällen verwenden Sie JSONeher Plain als JSONP.
Hippietrail
Ja, @hippietrail 2 Jahre machen einen großen Unterschied :-) CORS macht es definitiv möglich, aber natürlich muss die Datenquelle entsprechend eingerichtet sein.
Pointy
0

Sie können mit diesem Projekt einen CORS-Proxy verwenden . Der gesamte Datenverkehr wird an einen Endpunkt in Ihrer Domain geleitet und diese Informationen an eine externe Domain weitergeleitet. Da der Browser alle Anfragen registriert, um sich in derselben Domain zu befinden, können wir JSON veröffentlichen. HINWEIS: Dies funktioniert auch mit SSL-Zertifikaten, die auf dem Server gespeichert sind.

Eugene Scray
quelle
-1

Es gibt eine (Hack-) Lösung, die ich schon oft gemacht habe. Sie können mit JsonP posten. (Sie können ein Formular veröffentlichen, das größer als 2000 Zeichen ist, als Sie von GET verwenden können.)

Client-Anwendung 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);
  }
});

JAVA:

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

Auf diese Weise öffnen Sie Ihren Server für jede Post-Anfrage. Sie sollten dies erneut sichern, indem Sie eine Identität oder etwas anderes angeben.

Mit dieser Methode können Sie auch den Anforderungstyp von jsonp in json ändern. Beide funktionieren, stellen Sie einfach den richtigen Antwortinhaltstyp ein

jsonp

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

json

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

Bitte nicht, dass Ihr Server die SOP (Same Origin Policy) nicht mehr respektiert, aber wen interessiert das?

Dimitri Kopriwa
quelle
Dies ist nicht AJAX mit CORS. AJAX impliziert, dass Sie XML verwenden. Dies ist JSON [P] mit CORS. JSONP ist "JSON" mit "Padding". Wenn es JSON-Daten sendet, die mit einem Funktionsaufruf zum Auffüllen umschlossen sind, dann ist es JSONP mit CORS. Sie können sowohl JSON- als auch JSONP-Datennotationen verwenden, ohne nur <script>Tags in Ihr HTML-DOM einzufügen (Sie können sie sogar in Desktop-Apps verwenden, beispielsweise mehrere JSON-Anforderungen an denselben Server senden und den Funktionsnamen verwenden zum Beispiel als Anforderungsverfolgungs-ID).
BrainSlugs83
-6

Es ist möglich, hier ist meine Lösung:

In Ihrem Javascript:

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

In Ihrer url.php:

echo "handleRequest(".$responseData.")";
nosemaj
quelle
11
In diesem Fall hat jQuery Ihre Anfrage höchstwahrscheinlich gemäß der Dokumentation in Get umgewandelt: Hinweis: Dadurch werden POSTs zu GETs für Remotedomänenanforderungen. api.jquery.com/jQuery.ajax
OneWorld