Come fare una richiesta JSONP da Javascript senza JQuery?


122

Posso effettuare una richiesta JSONP interdominio in JavaScript senza utilizzare jQuery o un'altra libreria esterna? Vorrei utilizzare JavaScript stesso e quindi analizzare i dati e renderlo un oggetto in modo da poterlo utilizzare. Devo usare una libreria esterna? In caso contrario, come posso farlo?


Risposte:


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

2
Ecco un JSBin che può essere utilizzato per armeggiare con JSONP da Wikipedia. È stato fatto riferimento in questa risposta .
rkagerer

1
Penso che valga la pena sottolineare che la risposta dovrebbe essere del formato foo(payload_of_json_data):, l'idea è che quando viene caricato nel tag script, chiama la funzione foo con il payload già come oggetto javascript e non è necessaria alcuna analisi.
Octopus

@ WillMunn Non penso che sia fattibile con JSONP. È un trucco dei giorni precedenti a CORS. Per cosa hai bisogno di impostare le intestazioni? Il server deve specificamente accettare le richieste JSONP, quindi dovrebbe essere impostato per servire in modo sano.
Matt Ball

hai ragione, ho letto male un po 'di documentazione api, c'è un parametro di query speciale per fare quello che volevo quando uso appologies jsonp.
Will Munn

@ WillMunn non preoccuparti. Sono contento che tu sia riuscito a risolverlo!
Matt Ball

37

Esempio leggero (con supporto per onSuccess e onTimeout). È necessario passare il nome di richiamata all'interno dell'URL se necessario.

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

Utilizzo del campione:

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

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


29

Cos'è JSONP?

La cosa importante da ricordare con jsonp è che in realtà non è un protocollo o un tipo di dati. È solo un modo per caricare uno script al volo ed elaborare lo script introdotto nella pagina. Nello spirito di JSONP, ciò significa introdurre un nuovo oggetto javascript dal server nell'applicazione / script client.

Quando è necessario JSONP?

È un metodo per consentire a un dominio di accedere / elaborare i dati da un altro nella stessa pagina in modo asincrono. Principalmente, viene utilizzato per ignorare le restrizioni CORS (Cross Origin Resource Sharing) che si verificherebbero con una richiesta XHR (ajax). I caricamenti degli script non sono soggetti a restrizioni CORS.

Com'è fatto

L'introduzione di un nuovo oggetto javascript dal server può essere implementata in molti modi, ma la pratica più comune è che il server implementa l'esecuzione di una funzione di 'callback', con l'oggetto richiesto passato al suo interno. La funzione di callback è solo una funzione che hai già impostato sul client che lo script che carichi chiama nel punto in cui lo script viene caricato per elaborare i dati passati ad esso.

Esempio:

Ho un'applicazione che registra tutti gli elementi nella casa di qualcuno. La mia applicazione è impostata e ora desidero recuperare tutti gli oggetti nella camera da letto principale.

La mia applicazione è attiva app.home.com. Le API da cui devo caricare i dati sono attive api.home.com.

A meno che il server non sia impostato esplicitamente per consentirlo, non posso utilizzare ajax per caricare questi dati, poiché anche le pagine su sottodomini separati sono soggette a restrizioni XHR CORS.

Idealmente, impostare le cose per consentire XHR x-domain

Idealmente, poiché l'API e l'app si trovano sullo stesso dominio, potrei avere accesso per configurare le intestazioni su api.home.com. Se lo faccio, posso aggiungere un Access-Control-Allow-Origin: elemento di intestazione che concede l'accesso a app.home.com. Supponendo che l'intestazione sia impostata come segue Access-Control-Allow-Origin: "http://app.home.com":, questo è molto più sicuro dell'impostazione di JSONP. Questo perché app.home.compuò ottenere tutto ciò che vuole api.home.comsenza api.home.comdare a CORS l'accesso a tutta Internet.

La soluzione XHR di cui sopra non è possibile. Imposta JSONP Sul mio script client: ho impostato una funzione per elaborare la risposta dal server quando effettuo la chiamata JSONP. :

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

Il server dovrà essere impostato per restituire un mini script simile a "processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"Potrebbe essere progettato per restituire una stringa di questo tipo se //api.home.com?getdata=room&room=main_bedroomviene chiamato qualcosa di simile .

Quindi il client imposta un tag di script come tale:

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

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

Questo carica lo script e chiama immediatamente window.processJSONPResponse()come scritto / echo / stampato dal server. I dati passati come parametro alla funzione sono ora memorizzati nella dataFromServervariabile locale e puoi farci tutto ciò di cui hai bisogno.

Pulire

Una volta che il cliente ha i dati, ad es. immediatamente dopo che lo script è stato aggiunto al DOM, l'elemento script può essere rimosso dal DOM:

script.parentNode.removeChild(script);

2
Grazie mille, questo mi ha aiutato molto con il mio progetto. Un problema minore: ho ricevuto SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data. Dopo aver aggiunto virgolette singole ai dati, tutto ha funzionato bene, quindi:"processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"
Hein van Dyke

17

La mia comprensione è che in realtà utilizzi tag di script con JSONP, quindi ...

Il primo passo è creare la tua funzione che gestirà il JSON:

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

Assicurati che questa funzione sia accessibile a livello globale.

Successivamente, aggiungi un elemento di script al DOM:

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

Lo script caricherà il JavaScript che il provider API crea e lo eseguirà.


2
Grazie a tutti. L'ho trovato su Internet alla ricerca di alcuni dati e poi ci faccio qualcosa. Ho usato eval () sui dati di risposta che mi hanno aiutato a gestire l'array di oggetti che è (credo). {È stato un orso capire il parsing con le mie limitate capacità intellettuali ma alla fine ho ottenuto il valore da esso}. Fantastico.
Dave

@Dave @Matt Forse sono confuso su JSONP, ma non dovresti aver bisogno di evalo parseo altro. Dovresti ottenere JavaScript che il browser può semplicemente eseguire, giusto?
sdleihssirhc

Colpa mia, scusa per la confusione. Cercare di estrarre l'oggetto (valore? Proprietà?) Dall'array mi faceva girare la testa (annidamento, callback, array, elemento, oggetto, stringa, valore, parentesi graffe, parentesi ...). Ho rimosso il mio uso di eval e ho comunque ottenuto la proprietà (valore?) Dall'array (oggetto? Elemento?) Che volevo.
Dave

10

il modo in cui uso jsonp come di seguito:

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

quindi usa il metodo 'jsonp' in questo modo:

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

riferimento:

JavaScript XMLHttpRequest utilizzando JsonP

http://www.w3ctech.com/topic/721 (parla del modo di utilizzare Promise)


1
terminare gli incarichi script.src = src; Aggiungi il ';' alla fine di tutti gli incarichi
chdev77


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

Esempio di utilizzo:

const data = await load('http://api.github.com/orgs/kriasoft');

1
Non dimenticare di window[callback] = nullconsentire la raccolta dei rifiuti della funzione.
Sukima

3

Ho scritto una libreria per gestire questo, nel modo più semplice possibile. Non c'è bisogno di renderlo esterno, è solo una funzione. A differenza di altre opzioni, questo script si pulisce dopo se stesso ed è generalizzato per effettuare ulteriori richieste in fase di esecuzione.

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

2

Di seguito è riportato un JavaScriptesempio per effettuare una JSONPchiamata senza JQuery:

Inoltre, puoi fare riferimento al mio GitHubrepository per riferimento.

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


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

Perché window.document.getElementsByTagName('script')[0];e no document.body.appendChild(…)?
Sukima

Non dovrebbe logAPIessere impostato su nullquando fatto in modo che la raccolta dei rifiuti possa essere eseguita su di esso?
Sukima

0

Se stai usando ES6 con NPM, puoi provare il modulo del nodo "fetch-jsonp". Fetch API Fornisce supporto per effettuare una chiamata JsonP come una normale chiamata XHR.

Prerequisito: dovresti usare il isomorphic-fetchmodulo del nodo nel tuo stack.


0

Sto solo incollando una versione ES6 della bella risposta di 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)
    })
}
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.