Publier des données sur JsonP

102

Est-il possible de publier des données sur JsonP? Ou est-ce que toutes les données doivent être passées dans la chaîne de requête en tant que requête GET?

J'ai beaucoup de données à envoyer au service, à travers le domaine, et elles sont trop volumineuses pour être envoyées via la chaîne de requête

Quelles sont les options pour contourner ce problème?

ChrisCa
la source

Réponses:

83

Il n'est pas possible d'effectuer une opération asynchrone POSTavec un service sur un autre domaine, en raison de la limitation (assez sensible) de la même politique d'origine . JSON-P ne fonctionne que parce que vous êtes autorisé à insérer<script> balises dans le DOM et qu'elles peuvent pointer n'importe où.

Vous pouvez, bien entendu, faire d'une page sur un autre domaine l'action d'un POST de forme régulière.

Edit : Il existe des hacks intéressants si vous êtes prêt à faire beaucoup d'efforts pour insérer des <iframe>s cachés et vous amuser avec leurs propriétés.

friedo
la source
Vous avez mentionné qu'un "POST asynchrone" n'est pas possible .... puis-je faire un POST synchrone?
Mark
4
@mark "POST synchrone" signifie soumettre un formulaire qui utilise <form method = "post" action = "http: // ... / ...">
Steven Kryskalla
8
Ce n'est pas tout à fait vrai. Vous pouvez certainement envoyer des POSTrequêtes à d'autres domaines tant que ce domaine et votre navigateur sont pris en charge CORS. Mais c'est tout à fait vrai POSTet JSONPne sont pas compatibles.
hippietrail
2
JSONP est implémenté en insérant des <script>balises qui pointent vers un autre domaine. La seule façon d'exécuter des requêtes POST dans un navigateur est via des formulaires HTML ou XMLHttpRequest.
friedo
1
(en général -) Il est (!) possible de faire un POST asynchrone sur un service sur un autre domaine. la limitation concerne la réponse. la limitation concerne également la demande JSONP.
Royi Namir
20

Si vous avez besoin d'envoyer beaucoup de données entre domaines. Je crée généralement un service que vous pouvez appeler en deux étapes:

  1. Tout d'abord, le client effectue un envoi de FORMULAIRE (post autorisé cross domain). Le service stocke l'entrée dans la session sur le serveur (en utilisant le GUID comme clé). (le client crée un GUID et l'envoie dans le cadre de l'entrée)

  2. Ensuite, le client effectue une injection de script normale (JSONP) en tant que paramètre que vous utilisez le même GUID que celui que vous avez utilisé dans la publication FORM. Le service traite l'entrée de la session et renvoie les données à la manière JSONP normale. Après cela, la session est détruite.

Cela dépend bien sûr du fait que vous écrivez le serveur principal.

Par
la source
1
J'ai essayé votre approche. A travaillé pour FF14 et Chrome20. Opera11 et IE9 n'ont tout simplement pas transféré le message. (Vérifié avec leurs outils de débogage et écouté sur le serveur à l'autre bout) Peut-être lié au handicap de l'IE est cette question: stackoverflow.com/questions/10395803/… Chrome plainte dans la console, mais a quand même fait le POST: XMLHttpRequest ne peut pas load localhost: 8080 / xxx Origin null n'est pas autorisé par Access-Control-Allow-Origin.
OneWorld
@OneWorld - Vous n'avez pas fait ce que la réponse disait. XMLHttpRequestne devrait pas du tout être impliqué. La réponse de Per utilise une soumission de formulaire régulière pour effectuer la requête POST, puis une injection d'élément de script pour effectuer la requête GET.
Quentin
7

Je sais que c'est une sérieuse nécromancie, mais j'ai pensé publier mon implémentation de JSONP POST en utilisant jQuery, que j'utilise avec succès pour mon widget JS (ceci est utilisé pour l'enregistrement et la connexion des clients):

Fondamentalement, j'utilise une approche IFrame, comme suggéré dans la réponse acceptée. Ce que je fais différemment, c'est qu'après l'envoi de la demande, je regarde si le formulaire peut être atteint dans l'iframe, à l'aide d'un minuteur. Lorsque le formulaire ne peut être atteint, cela signifie que la demande est de retour. Ensuite, j'utilise une requête JSONP normale pour demander le statut de l'opération.

J'espère que quelqu'un le trouvera utile. Testé dans> = IE8, Chrome, FireFox et 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
la source
4

En général, JSONP est implémenté en ajoutant un <script> balise au document appelant, de sorte que l'URL du service JSONP soit le "src". Le navigateur récupère la source du script avec une transaction HTTP GET.

Maintenant, si votre service JSONP est dans le même domaine que votre page d'appel, vous pourriez probablement bricoler quelque chose avec un simple $.ajax()appel. Si ce n'est pas dans le même domaine, je ne sais pas comment cela serait possible.

Pointu
la source
Ce n'est pas dans le même domaine dans ce cas. Et je suppose que seul GET est possible, mais je voulais vérifier car je n'ai commencé à lire sur JsonP qu'aujourd'hui et je dois prendre des décisions pour savoir s'il convient à ce dont j'ai besoin
ChrisCa
2
Si ce n'est pas dans le même domaine mais qu'il le prend en charge, CORScela sera possible tant que le navigateur le prend également en charge. Dans ces cas, vous utiliserez plain JSONplutôt que JSONP.
hippietrail
Oui, @hippietrail 2 ans fait une grande différence :-) CORS le rend certainement possible, mais bien sûr, cela nécessite que la source de données soit configurée de manière appropriée.
Pointy
0

Vous pouvez utiliser un proxy CORS en utilisant ce projet . Il dirigerait tout le trafic vers un point de terminaison de votre domaine et relayerait ces informations vers un domaine externe. Étant donné que le navigateur enregistre toutes les demandes pour être sur le même domaine, nous pouvons publier JSON. REMARQUE: cela fonctionne également avec les certificats SSL détenus sur le serveur.

Eugène Scray
la source
-1

Il y a une solution (de piratage) que j'ai faite plusieurs fois, vous pourrez publier avec JsonP. (Vous pourrez publier un formulaire, plus grand que 2000 caractères que vous pouvez utiliser par GET)

Javascript de l'application client

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

En faisant comme cela, vous ouvrez votre serveur à toute demande de publication, vous devez le sécuriser à nouveau en fournissant ident ou autre chose.

Avec cette méthode, vous pouvez également changer le type de demande de jsonp à json, les deux fonctionnent, il suffit de définir le bon type de contenu de réponse

jsonp

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

json

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

Veuillez noter que votre serveur ne respectera plus la SOP (même politique d'origine), mais qu'importe?

Dimitri Kopriwa
la source
Ce n'est pas AJAX avec CORS. AJAX implique que vous utilisez XML. C'est JSON [P] avec CORS. JSONP est "JSON" avec "Padding". S'il envoie des données JSON, encapsulées avec un appel de fonction pour le remplissage, alors c'est JSONP avec CORS. Vous pouvez utiliser à la fois des notations de données JSON et JSONP en dehors de l'injection de <script>balises dans votre DOM HTML (vous pouvez même les utiliser dans des applications de bureau, disons que vous vouliez faire plusieurs demandes JSON au même serveur et que vous vouliez utiliser le nom de la fonction comme ID de suivi de demande par exemple).
BrainSlugs83
-6

C'est possible, voici ma solution:

Dans votre javascript:

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

Dans votre url.php:

echo "handleRequest(".$responseData.")";
nosemaj
la source
11
Dans ce cas, jQuery a très probablement transformé votre demande en Get selon sa documentation: Remarque: Cela transformera les POST en GET pour les demandes de domaine distant. api.jquery.com/jQuery.ajax
OneWorld