Wie erstelle ich eine JSONP-Anfrage aus Javascript ohne JQuery?

121

Kann ich eine domänenübergreifende JSONP-Anfrage in JavaScript stellen, ohne jQuery oder eine andere externe Bibliothek zu verwenden? Ich möchte JavaScript selbst verwenden und dann die Daten analysieren und daraus ein Objekt machen, damit ich sie verwenden kann. Muss ich eine externe Bibliothek verwenden? Wenn nicht, wie kann ich das machen?

Dave
quelle

Antworten:

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
quelle
2
Hier ist ein JSBin, mit dem Sie mit JSONP aus Wikipedia experimentieren können . In dieser Antwort wurde darauf verwiesen .
Rkagerer
1
Ich denke, es ist erwähnenswert, dass die Antwort die Form haben sollte: foo(payload_of_json_data)Die Idee ist, dass beim Laden in das Skript-Tag die Funktion foo mit der Nutzlast bereits als Javascript-Objekt aufgerufen wird und keine Analyse erforderlich ist.
Octopus
@ WillMunn Ich glaube nicht, dass das mit JSONP machbar ist. Es ist ein Hack aus der Zeit vor CORS. Wofür müssen Sie Header festlegen? Der Server muss speziell JSONP-Anforderungen akzeptieren, daher sollte er so eingerichtet sein, dass er ordnungsgemäß bedient wird.
Matt Ball
Sie haben Recht, ich habe einige API-Dokumentationen falsch gelesen. Es gibt einen speziellen Abfrageparameter, um das zu tun, was ich wollte, wenn ich JSONP-Apps verwende.
Will Munn
@ WillMunn keine Sorge. Ich bin froh, dass du es geschafft hast!
Matt Ball
37

Leichtes Beispiel (mit Unterstützung für onSuccess und onTimeout). Sie müssen den Rückrufnamen innerhalb der URL übergeben, wenn Sie ihn benötigen.

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

Beispielnutzung:

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

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

sobstel
quelle
28

Was ist JSONP?

Das Wichtigste bei jsonp ist, dass es sich nicht um ein Protokoll oder einen Datentyp handelt. Es ist nur eine Möglichkeit, ein Skript im laufenden Betrieb zu laden und das auf der Seite eingeführte Skript zu verarbeiten. Im Sinne von JSONP bedeutet dies, dass ein neues Javascript-Objekt vom Server in die Client-Anwendung / das Client-Skript eingefügt wird.

Wann wird JSONP benötigt?

Dies ist eine Methode, mit der eine Domäne asynchron auf Daten von einer anderen Domäne auf derselben Seite zugreifen / diese verarbeiten kann. In erster Linie wird es verwendet, um CORS-Einschränkungen (Cross Origin Resource Sharing) zu überschreiben, die bei einer XHR-Anforderung (Ajax) auftreten würden. Das Laden von Skripten unterliegt keinen CORS-Einschränkungen.

Wie wird es gemacht

Das Einführen eines neuen Javascript-Objekts vom Server kann auf viele Arten implementiert werden. Am häufigsten wird jedoch die Ausführung einer Rückruffunktion durch den Server implementiert, wobei das erforderliche Objekt übergeben wird. Die Rückruffunktion ist nur eine Funktion, die Sie bereits auf dem Client eingerichtet haben und die das geladene Skript an dem Punkt aufruft, an dem das Skript geladen wird , um die an ihn übergebenen Daten zu verarbeiten.

Beispiel:

Ich habe eine Anwendung, die alle Elemente bei jemandem zu Hause protokolliert. Meine Anwendung ist eingerichtet und ich möchte jetzt alle Elemente im Hauptschlafzimmer abrufen.

Meine Bewerbung ist eingeschaltet app.home.com. Die APIs, von denen ich Daten laden muss, sind aktiviert api.home.com.

Sofern der Server nicht explizit so eingerichtet ist, dass er dies zulässt, kann ich diese Daten nicht mit Ajax laden, da selbst Seiten in separaten Subdomänen XHR CORS-Einschränkungen unterliegen.

Richten Sie im Idealfall die XHR-XHR ein

Da sich die API und die App in derselben Domain befinden, kann ich im Idealfall die Header einrichten api.home.com. In diesem Fall kann ich ein Access-Control-Allow-Origin: Header-Element hinzufügen , das Zugriff gewährt app.home.com. Angenommen, der Header ist wie folgt eingerichtet: Access-Control-Allow-Origin: "http://app.home.com"Dies ist weitaus sicherer als das Einrichten von JSONP. Dies liegt daran, app.home.comdass Sie alles bekommen können, was Sie wollen, api.home.comohne api.home.comCORS Zugang zum gesamten Internet zu gewähren.

Die obige XHR-Lösung ist nicht möglich. Einrichten von JSONP In meinem Client-Skript: Ich habe eine Funktion eingerichtet, um die Antwort vom Server zu verarbeiten, wenn ich den JSONP-Aufruf tätige . ::

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

Der Server muss so eingerichtet sein, dass er ein Mini-Skript zurückgibt, das ungefähr so ​​aussieht. "processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"Es könnte so konzipiert sein, dass es eine solche Zeichenfolge zurückgibt, wenn so etwas //api.home.com?getdata=room&room=main_bedroomaufgerufen wird.

Anschließend richtet der Client ein Skript-Tag als solches ein:

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

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

Dies lädt das Skript und ruft sofort window.processJSONPResponse()als vom Server geschrieben / echo / ausgedruckt auf. Die als Parameter an die Funktion übergebenen Daten werden jetzt in der dataFromServerlokalen Variablen gespeichert und Sie können damit alles tun, was Sie brauchen.

Aufräumen

Sobald der Kunde die Daten hat, dh. Unmittelbar nach dem Hinzufügen des Skripts zum DOM kann das Skriptelement aus dem DOM entfernt werden:

script.parentNode.removeChild(script);
tau
quelle
1
Vielen Dank, das hat mir bei meinem Projekt sehr geholfen. Ein kleines Problem: Ich habe erhalten SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data. Nach dem Hinzufügen von einfachen Anführungszeichen zu den Daten funktionierte alles einwandfrei, also:"processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"
Hein van Dyke
17

Mein Verständnis ist, dass Sie tatsächlich Skript-Tags mit JSONP verwenden, sooo ...

Der erste Schritt besteht darin, eine Funktion zu erstellen, die den JSON verarbeitet:

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

Stellen Sie sicher, dass auf diese Funktion auf globaler Ebene zugegriffen werden kann.

Fügen Sie als Nächstes dem DOM ein Skriptelement hinzu:

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

Das Skript lädt das vom API-Anbieter erstellte JavaScript und führt es aus.

sdleihssirhc
quelle
2
Vielen Dank an alle. Ich habe es über das Internet auf der Suche nach Daten herausgeschossen und dann mache ich etwas damit. Ich habe eval () für die Antwortdaten verwendet, was mir geholfen hat, mit dem Objektarray umzugehen, das es ist (glaube ich). {Es war ein Bär, der das Parsen mit meiner begrenzten Gehirnleistung herausgefunden hat, aber ich habe endlich den Wert daraus gezogen}. Fantastisch.
Dave
@ Dave @ Matt Vielleicht Ich bin auf JSONP flockig, aber Sie sollten brauchen nicht evaloder parseoder nichts. Sie sollten JavaScript erhalten, das der Browser gerade ausführen kann, oder?
sdleihssirhc
Mein schlechtes, entschuldige die Verwirrung. Der Versuch, das Ding (Wert? Eigenschaft?) Aus dem Array herauszuholen, ließ meinen Kopf drehen (Verschachtelung, Rückruf, Array, Element, Objekt, Zeichenfolge, Wert, geschweifte Klammern, Klammern ...). Ich habe die Verwendung von eval entfernt und trotzdem die Eigenschaft (Wert?) Aus dem Array (Objekt? Element?) Erhalten, das ich wollte.
Dave
10

die Art und Weise, wie ich jsonp wie folgt benutze:

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

Verwenden Sie dann die 'jsonp'-Methode wie folgt:

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

Referenz:

JavaScript XMLHttpRequest mit JsonP

http://www.w3ctech.com/topic/721 (sprechen Sie über die Art der Verwendung Versprechen)

Derek
quelle
1
Beenden Sie die Zuweisungen script.src = src; Ergänzen Sie die ';' bis zum Ende aller Aufgaben
chdev77
6

Ich habe eine reine Javascript-Bibliothek, um das zu tun https://github.com/robertodecurnex/J50Npi/blob/master/J50Npi.js

Schauen Sie es sich an und lassen Sie mich wissen, wenn Sie Hilfe beim Verwenden oder Verstehen des Codes benötigen.

Übrigens haben Sie hier ein einfaches Anwendungsbeispiel: http://robertodecurnex.github.com/J50Npi/

robertodecurnex
quelle
6
Ihre Lösung war für meinen Anwendungsfall etwas zu einfach, daher habe ich Unterstützung für
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);
  });
})();

Anwendungsbeispiel:

const data = await load('http://api.github.com/orgs/kriasoft');
Konstantin Tarkus
quelle
1
Vergessen Sie nicht, window[callback] = nulldie Funktion als Müllsammler zuzulassen.
Sukima
3

Ich habe eine Bibliothek geschrieben, um so einfach wie möglich damit umzugehen. Keine Notwendigkeit, es extern zu machen, es ist nur eine Funktion. Im Gegensatz zu einigen anderen Optionen bereinigt dieses Skript nach sich selbst und ist verallgemeinert, um zur Laufzeit weitere Anforderungen zu stellen.

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
quelle
2

Im folgenden JavaScriptBeispiel können Sie einen JSONPAnruf ohne JQuery tätigen :

Sie können auch mein GitHubRepository als Referenz referenzieren.

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.
quelle
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
quelle
Warum window.document.getElementsByTagName('script')[0];und nicht document.body.appendChild(…)?
Sukima
Sollte nicht logAPIeingestellt werden, nullwenn dies erledigt ist, damit die Speicherbereinigung durchgeführt werden kann?
Sukima
0

Wenn Sie ES6 mit NPM verwenden, können Sie das Knotenmodul "fetch-jsonp" ausprobieren. Abruf-API Bietet Unterstützung für das Tätigen eines JsonP-Aufrufs als regulären XHR-Aufruf.

Voraussetzung: Sie sollten das isomorphic-fetchKnotenmodul in Ihrem Stapel verwenden.

Rajendra Kumar Vankadari
quelle
0

Fügen Sie einfach eine ES6-Version von Sobstels netter Antwort ein:

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
quelle