Comment faire une requête JSONP depuis Javascript sans JQuery?

122

Puis-je effectuer une requête JSONP inter-domaines en JavaScript sans utiliser jQuery ou une autre bibliothèque externe? J'aimerais utiliser JavaScript lui-même, puis analyser les données et en faire un objet afin que je puisse l'utiliser. Dois-je utiliser une bibliothèque externe? Sinon, comment puis-je le faire?

Dave
la source

Réponses:

151
function foo(data)
{
    // do stuff with JSON
}

var script = document.createElement('script');
script.src = '//example.com/path/to/jsonp?callback=foo'

document.getElementsByTagName('head')[0].appendChild(script);
// or document.head.appendChild(script) in modern browsers
Matt Ball
la source
2
Voici un JSBin qui peut être utilisé pour jouer avec JSONP de Wikipedia. Il a été mentionné dans cette réponse .
rkagerer
1
Je pense qu'il vaut la peine de souligner que la réponse doit être de la forme:, foo(payload_of_json_data)l'idée étant que lorsqu'elle est chargée dans la balise de script, elle appelle la fonction foo avec la charge utile déjà en tant qu'objet javascript et aucune analyse n'est nécessaire.
Octopus
@WillMunn Je ne pense pas que ce soit faisable avec JSONP. C'est un hack des jours pré-CORS. Pour quoi avez-vous besoin de définir des en-têtes? Le serveur doit spécifiquement accepter les demandes JSONP, il doit donc être configuré pour servir de manière saine.
Matt Ball
vous avez raison, j'ai mal lu la documentation de l'API, il existe un paramètre de requête spécial pour faire ce que je voulais lors de l'utilisation des applications jsonp.
Will Munn
@WillMunn pas de soucis. Heureux que vous ayez pu régler le problème!
Matt Ball
37

Exemple léger (avec prise en charge de onSuccess et onTimeout). Vous devez transmettre le nom de rappel dans l'URL si vous en avez besoin.

var $jsonp = (function(){
  var that = {};

  that.send = function(src, options) {
    var callback_name = options.callbackName || 'callback',
      on_success = options.onSuccess || function(){},
      on_timeout = options.onTimeout || function(){},
      timeout = options.timeout || 10; // sec

    var timeout_trigger = window.setTimeout(function(){
      window[callback_name] = function(){};
      on_timeout();
    }, timeout * 1000);

    window[callback_name] = function(data){
      window.clearTimeout(timeout_trigger);
      on_success(data);
    }

    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.async = true;
    script.src = src;

    document.getElementsByTagName('head')[0].appendChild(script);
  }

  return that;
})();

Exemple d'utilisation:

$jsonp.send('some_url?callback=handleStuff', {
    callbackName: 'handleStuff',
    onSuccess: function(json){
        console.log('success!', json);
    },
    onTimeout: function(){
        console.log('timeout!');
    },
    timeout: 5
});

Sur GitHub: https://github.com/sobstel/jsonp.js/blob/master/jsonp.js

sobstel
la source
29

Qu'est-ce que JSONP?

La chose importante à retenir avec jsonp est qu'il ne s'agit pas réellement d'un protocole ou d'un type de données. C'est juste un moyen de charger un script à la volée et de traiter le script qui est introduit dans la page. Dans l'esprit de JSONP, cela signifie introduire un nouvel objet javascript du serveur dans l'application / script client.

Quand JSONP est-il nécessaire?

C'est une méthode permettant à un domaine d'accéder / traiter les données d'un autre dans la même page de manière asyncronale. Principalement, il est utilisé pour remplacer les restrictions CORS (Cross Origin Resource Sharing) qui se produiraient avec une requête XHR (ajax). Les chargements de scripts ne sont pas soumis aux restrictions CORS.

Comment est-il fait

L'introduction d'un nouvel objet javascript à partir du serveur peut être implémentée de plusieurs manières, mais la pratique la plus courante consiste pour le serveur à implémenter l'exécution d'une fonction de «rappel», avec l'objet requis passé. La fonction de rappel est simplement une fonction que vous avez déjà configurée sur le client et que le script que vous chargez appelle au moment où le script se charge pour traiter les données qui lui sont transmises.

Exemple:

J'ai une application qui enregistre tous les éléments dans la maison de quelqu'un. Mon application est configurée et je souhaite maintenant récupérer tous les éléments de la chambre principale.

Ma candidature est activée app.home.com. Les API dont j'ai besoin pour charger des données sont activées api.home.com.

À moins que le serveur ne soit explicitement configuré pour le permettre, je ne peux pas utiliser ajax pour charger ces données, car même les pages sur des sous-domaines séparés sont soumises aux restrictions XHR CORS.

Idéalement, configurez les choses pour autoriser x-domain XHR

Idéalement, comme l'API et l'application sont sur le même domaine, je pourrais avoir accès pour configurer les en-têtes api.home.com. Si je le fais, je peux ajouter un Access-Control-Allow-Origin: élément d'en-tête donnant accès à app.home.com. En supposant que l'en-tête est configuré comme suit Access-Control-Allow-Origin: "http://app.home.com":, c'est beaucoup plus sûr que la configuration de JSONP. En effet, il app.home.compeut obtenir tout ce dont il a api.home.combesoin sans api.home.comdonner à CORS l'accès à tout Internet.

La solution XHR ci-dessus n'est pas possible. Configurer JSONP Sur mon script client: J'ai configuré une fonction pour traiter la réponse du serveur lorsque j'effectue l'appel JSONP. :

function processJSONPResponse(data) {
    var dataFromServer = data;
}

Le serveur devra être configuré pour renvoyer un mini-script ressemblant à quelque chose comme "processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"Il pourrait être conçu pour renvoyer une telle chaîne si quelque chose comme //api.home.com?getdata=room&room=main_bedroomest appelé.

Ensuite, le client configure une balise de script en tant que telle:

var script = document.createElement('script');
script.src = '//api.home.com?getdata=room&room=main_bedroom';

document.querySelector('head').appendChild(script);

Cela charge le script et l'appelle immédiatement window.processJSONPResponse()comme écrit / écho / imprimé par le serveur. Les données passées en paramètre à la fonction sont maintenant stockées dans la dataFromServervariable locale et vous pouvez en faire ce dont vous avez besoin.

Nettoyer

Une fois que le client a les données, c'est à dire. immédiatement après l'ajout du script au DOM, l'élément de script peut être supprimé du DOM:

script.parentNode.removeChild(script);
rosée
la source
2
Merci beaucoup, cela m'a beaucoup aidé dans mon projet. Un problème mineur: j'ai reçu SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data. Après avoir ajouté des guillemets simples aux données, tout a bien fonctionné, donc:"processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"
Hein van Dyke
17

Je crois comprendre que vous utilisez en fait des balises de script avec JSONP, sooo ...

La première étape consiste à créer votre fonction qui gérera le JSON:

function hooray(json) {
    // dealin wit teh jsonz
}

Assurez-vous que cette fonction est accessible au niveau global.

Ensuite, ajoutez un élément de script au DOM:

var script = document.createElement('script');
script.src = 'http://domain.com/?function=hooray';
document.body.appendChild(script);

Le script chargera le JavaScript créé par le fournisseur d'API et l'exécutera.

sdleihssirhc
la source
2
Merci tout le monde. Je l'ai filmé sur Internet à la recherche de données, puis je fais quelque chose avec. J'ai utilisé eval () sur les données de réponse, ce qui m'a aidé à gérer l'objet - tableau qu'il est (je pense). {C'était un ours qui a compris l'analyse avec mes capacités intellectuelles limitées, mais j'en ai finalement tiré la valeur}. Fantastique.
Dave
Peut - être que je @ Dave @ Matt suis floue sur JSONP, mais vous ne devriez pas avoir besoin evalou parseou quoi que ce soit. Vous devriez obtenir du JavaScript que le navigateur peut simplement exécuter, non?
sdleihssirhc
Mon mal, désolé pour la confusion. Essayer d'extraire la chose (valeur? Propriété?) Du tableau me faisait tourner la tête (l'imbrication, le rappel, le tableau, l'élément, l'objet, la chaîne, la valeur, les accolades, les crochets ...). J'ai supprimé mon utilisation de eval et j'ai toujours obtenu la propriété (valeur?) Du tableau (objet? Élément?) Que je voulais.
Dave
10

la façon dont j'utilise jsonp comme ci-dessous:

function jsonp(uri) {
    return new Promise(function(resolve, reject) {
        var id = '_' + Math.round(10000 * Math.random());
        var callbackName = 'jsonp_callback_' + id;
        window[callbackName] = function(data) {
            delete window[callbackName];
            var ele = document.getElementById(id);
            ele.parentNode.removeChild(ele);
            resolve(data);
        }

        var src = uri + '&callback=' + callbackName;
        var script = document.createElement('script');
        script.src = src;
        script.id = id;
        script.addEventListener('error', reject);
        (document.getElementsByTagName('head')[0] || document.body || document.documentElement).appendChild(script)
    });
}

puis utilisez la méthode 'jsonp' comme ceci:

jsonp('http://xxx/cors').then(function(data){
    console.log(data);
});

référence:

JavaScript XMLHttpRequest utilisant JsonP

http://www.w3ctech.com/topic/721 (parler de la façon d'utiliser Promise)

Derek
la source
1
terminer les affectations script.src = src; ajouter le ';' à la fin de toutes les missions
chdev77
6

J'ai une bibliothèque javascript pure pour le faire https://github.com/robertodecurnex/J50Npi/blob/master/J50Npi.js

Jetez-y un œil et faites-moi savoir si vous avez besoin d'aide pour utiliser ou comprendre le code.

Btw, vous avez un exemple d'utilisation simple ici: http://robertodecurnex.github.com/J50Npi/

robertodecurnex
la source
6
Votre solution était un peu trop simple pour mon cas d'utilisation, j'ai donc ajouté le support de requêtes
Chad Scira
5
/**
 * Loads data asynchronously via JSONP.
 */
const load = (() => {
  let index = 0;
  const timeout = 5000;

  return url => new Promise((resolve, reject) => {
    const callback = '__callback' + index++;
    const timeoutID = window.setTimeout(() => {
      reject(new Error('Request timeout.'));
    }, timeout);

    window[callback] = response => {
      window.clearTimeout(timeoutID);
      resolve(response.data);
    };

    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.async = true;
    script.src = url + (url.indexOf('?') === -1 ? '?' : '&') + 'callback=' + callback;
    document.getElementsByTagName('head')[0].appendChild(script);
  });
})();

Exemple d'utilisation:

const data = await load('http://api.github.com/orgs/kriasoft');
Konstantin Tarkus
la source
1
N'oubliez pas window[callback] = nulld'autoriser le ramassage des ordures de la fonction.
Sukima
3

J'ai écrit une bibliothèque pour gérer cela, aussi simplement que possible. Pas besoin de le rendre externe, c'est juste une fonction. Contrairement à certaines autres options, ce script nettoie après lui-même et est généralisé pour faire d'autres requêtes lors de l'exécution.

https://github.com/Fresheyeball/micro-jsonp

function jsonp(url, key, callback) {

    var appendParam = function(url, key, param){
            return url
                + (url.indexOf("?") > 0 ? "&" : "?")
                + key + "=" + param;
        },

        createScript = function(url, callback){
            var doc = document,
                head = doc.head,
                script = doc.createElement("script");

            script
            .setAttribute("src", url);

            head
            .appendChild(script);

            callback(function(){
                setTimeout(function(){
                    head
                    .removeChild(script);
                }, 0);
            });
        },

        q =
            "q" + Math.round(Math.random() * Date.now());

    createScript(
        appendParam(url, key, q), function(remove){
            window[q] =
                function(json){
                    window[q] = undefined;
                    remove();
                    callback(json);
                };
        });
}
Fresheyeball
la source
2

Veuillez trouver ci-dessous un JavaScriptexemple pour passer un JSONPappel sans JQuery:

En outre, vous pouvez consulter mon GitHubréférentiel pour référence.

https://github.com/shedagemayur/JavaScriptCode/tree/master/jsonp

window.onload = function(){
    var callbackMethod = 'callback_' + new Date().getTime();

    var script = document.createElement('script');
    script.src = 'https://jsonplaceholder.typicode.com/users/1?callback='+callbackMethod;

    document.body.appendChild(script);

    window[callbackMethod] = function(data){
        delete window[callbackMethod];
        document.body.removeChild(script);
        console.log(data);
    }
}

Mayur S
la source
0
/**
 * Get JSONP data for cross-domain AJAX requests
 * @private
 * @link http://cameronspear.com/blog/exactly-what-is-jsonp/
 * @param  {String} url      The URL of the JSON request
 * @param  {String} callback The name of the callback to run on load
 */
var loadJSONP = function ( url, callback ) {

    // Create script with url and callback (if specified)
    var ref = window.document.getElementsByTagName( 'script' )[ 0 ];
    var script = window.document.createElement( 'script' );
    script.src = url + (url.indexOf( '?' ) + 1 ? '&' : '?') + 'callback=' + callback;

    // Insert script tag into the DOM (append to <head>)
    ref.parentNode.insertBefore( script, ref );

    // After the script is loaded (and executed), remove it
    script.onload = function () {
        this.remove();
    };

};

/** 
 * Example
 */

// Function to run on success
var logAPI = function ( data ) {
    console.log( data );
}

// Run request
loadJSONP( 'http://api.petfinder.com/shelter.getPets?format=json&key=12345&shelter=AA11', 'logAPI' );
Chris Ferdinandi
la source
Pourquoi window.document.getElementsByTagName('script')[0];et pas document.body.appendChild(…)?
Sukima
Ne devrait pas logAPIêtre défini sur une nullfois terminé pour que le garbage collection puisse y être effectué?
Sukima
0

Si vous utilisez ES6 avec NPM, vous pouvez essayer le module de nœud "fetch-jsonp". Fetch API Fournit une prise en charge pour effectuer un appel JsonP en tant qu'appel XHR normal.

Prérequis: vous devez utiliser le isomorphic-fetchmodule node dans votre pile.

Rajendra kumar Vankadari
la source
0

Je viens de coller une version ES6 de la belle réponse de Sobstel:

send(someUrl + 'error?d=' + encodeURI(JSON.stringify(json)) + '&callback=c', 'c', 5)
    .then((json) => console.log(json))
    .catch((err) => console.log(err))

function send(url, callback, timeout) {
    return new Promise((resolve, reject) => {
        let script = document.createElement('script')
        let timeout_trigger = window.setTimeout(() => {
            window[callback] = () => {}
            script.parentNode.removeChild(script)
            reject('No response')
        }, timeout * 1000)

        window[callback] = (data) => {
            window.clearTimeout(timeout_trigger)
            script.parentNode.removeChild(script)
            resolve(data)
        }

        script.type = 'text/javascript'
        script.async = true
        script.src = url

        document.getElementsByTagName('head')[0].appendChild(script)
    })
}
just_user
la source