Jak wykonać żądanie JSONP z Javascript bez JQuery?

122

Czy mogę wysłać żądanie JSONP między domenami w JavaScript bez korzystania z jQuery lub innej zewnętrznej biblioteki? Chciałbym użyć samego JavaScript, a następnie przeanalizować dane i uczynić z nich obiekt, aby móc go używać. Czy muszę korzystać z zewnętrznej biblioteki? Jeśli nie, jak mogę to zrobić?

Dave
źródło

Odpowiedzi:

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
źródło
2
Oto JSBin, którego można używać do majstrowania przy JSONP z Wikipedii. Zostało przywołane w tej odpowiedzi .
rkagerer
1
Myślę, że warto zwrócić uwagę, że odpowiedź powinna mieć postać foo(payload_of_json_data):, chodzi o to, że po załadowaniu do tagu skryptu wywołuje funkcję foo z ładunkiem już jako obiekt javascript i nie jest konieczne żadne analizowanie.
Octopus
@WillMunn Nie sądzę, żeby było to wykonalne z JSONP. To hack z czasów przed CORS. Do czego potrzebujesz ustawić nagłówki? Serwer musi akceptować żądania JSONP, więc powinien być skonfigurowany do obsługi w rozsądny sposób.
Matt Ball
masz rację, źle odczytałem dokumentację API, jest specjalny parametr zapytania, który robi to, co chciałem, używając aplikacji jsonp.
Will Munn
@WillMunn nie ma obaw. Cieszę się, że udało Ci się to rozwiązać!
Matt Ball
37

Lekki przykład (z obsługą onSuccess i onTimeout). Jeśli potrzebujesz, musisz podać nazwę zwrotną w adresie URL.

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

Przykładowe użycie:

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

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

sobstel
źródło
29

Co to jest JSONP?

Ważną rzeczą do zapamiętania w przypadku jsonp jest to, że w rzeczywistości nie jest to protokół ani typ danych. Jest to po prostu sposób na załadowanie skryptu w locie i przetworzenie skryptu wprowadzanego na stronę. W duchu JSONP oznacza to wprowadzenie nowego obiektu javascript z serwera do aplikacji / skryptu klienta.

Kiedy potrzebny jest JSONP?

Jest to jedna z metod umożliwiania jednej domenie asynchronicznego dostępu do danych z innej domeny / przetwarzania ich na tej samej stronie. Przede wszystkim służy do przesłonięcia ograniczeń CORS (Cross Origin Resource Sharing), które wystąpiłyby w przypadku żądania XHR (ajax). Ładowanie skryptów nie podlega ograniczeniom CORS.

Jak to jest zrobione

Wprowadzenie nowego obiektu javascript z serwera można zaimplementować na wiele sposobów, ale najczęstszą praktyką jest zaimplementowanie przez serwer funkcji „callback” z przekazaniem do niej wymaganego obiektu. Funkcja zwrotna to po prostu funkcja, którą już skonfigurowałeś na kliencie, którą ładowany skrypt wywołuje w momencie ładowania skryptu w celu przetworzenia przekazanych do niego danych.

Przykład:

Mam aplikację, która rejestruje wszystkie pozycje w czyimś domu. Moja aplikacja jest skonfigurowana i chcę teraz pobrać wszystkie przedmioty w głównej sypialni.

Moja aplikacja jest włączona app.home.com. Interfejs API, z którego chcę załadować dane, jest włączony api.home.com.

O ile serwer nie jest wyraźnie skonfigurowany, aby na to zezwolić, nie mogę użyć Ajax do ładowania tych danych, ponieważ nawet strony w oddzielnych subdomenach podlegają ograniczeniom XHR CORS.

W idealnym przypadku skonfiguruj rzeczy tak, aby umożliwić XHR

W idealnym przypadku, ponieważ interfejs API i aplikacja znajdują się w tej samej domenie, mógłbym mieć dostęp do skonfigurowania nagłówków api.home.com. Jeśli tak, mogę dodać element Access-Control-Allow-Origin: nagłówka zapewniający dostęp do app.home.com. Zakładając, że nagłówek jest skonfigurowany w następujący sposób: Access-Control-Allow-Origin: "http://app.home.com"jest to znacznie bezpieczniejsze niż konfiguracja JSONP. Dzieje się tak, ponieważ app.home.commoże uzyskać wszystko, czego chce, api.home.combez api.home.comzapewniania CORS dostępu do całego Internetu.

Powyższe rozwiązanie XHR nie jest możliwe. Skonfiguruj JSONP Na moim skrypcie klienta: Ustawiłem funkcję przetwarzania odpowiedzi z serwera, gdy wykonuję wywołanie JSONP. :

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

Serwer będzie musiał być tak skonfigurowany, aby zwracał mini skrypt wyglądający jakoś. "processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"Może być przeznaczony do zwracania takiego ciągu, jeśli //api.home.com?getdata=room&room=main_bedroomzostanie wywołane coś podobnego .

Następnie klient konfiguruje tag skryptu jako taki:

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

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

To ładuje skrypt i natychmiast wywołuje window.processJSONPResponse()jako napisane / echo / wydrukowane przez serwer. Dane przekazane jako parametr do funkcji są teraz przechowywane w dataFromServerzmiennej lokalnej i możesz z nimi zrobić, co chcesz.

Sprzątać

Gdy klient ma już dane tj. natychmiast po dodaniu skryptu do DOM, element skryptu można usunąć z DOM:

script.parentNode.removeChild(script);
dewd
źródło
2
Wielkie dzięki, bardzo pomogło mi to w moim projekcie. Drobny problem: otrzymałem SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data. Po dodaniu pojedynczych cudzysłowów do danych wszystko działało dobrze, więc:"processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"
Hein van Dyke
17

Rozumiem, że faktycznie używasz tagów skryptu z JSONP, więc ...

Pierwszym krokiem jest utworzenie funkcji, która będzie obsługiwać JSON:

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

Upewnij się, że ta funkcja jest dostępna na poziomie globalnym.

Następnie dodaj element skryptu do DOM:

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

Skrypt załaduje skrypt JavaScript, który tworzy dostawca interfejsu API, i wykona go.

sdleihssirhc
źródło
2
Dziękuję wszystkim. Rozbiłem się przez internet w poszukiwaniu jakichś danych, a potem coś z tym robię. Użyłem eval () na danych odpowiedzi, które pomogły mi poradzić sobie z obiektem - tablicą, która to jest (myślę). {To był niedźwiedź zastanawiający się nad parsowaniem z moją ograniczoną siłą mózgu, ale w końcu wyciągnąłem z tego wartość}. Fantastyczny.
Dave
@Dave @Matt Może ja jestem rozmyte na jsonp, ale nie ma potrzeby evallub parseczy czegokolwiek. Powinieneś dostać JavaScript, który przeglądarka może po prostu wykonać, prawda?
sdleihssirhc
Przepraszam za zamieszanie. Próba wyciągnięcia rzeczy (wartość? Właściwość?) Z tablicy przyprawiała mnie o zawrót głowy (Zagnieżdżanie, wywołanie zwrotne, tablica, element, obiekt, ciąg znaków, wartość, nawiasy klamrowe, nawiasy ...). Usunąłem użycie eval i nadal otrzymałem właściwość (wartość?) Z tablicy (obiekt? Element?).
Dave
10

sposób, w jaki używam jsonp jak poniżej:

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

następnie użyj metody 'jsonp' w następujący sposób:

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

odniesienie:

JavaScript XMLHttpRequest przy użyciu JsonP

http://www.w3ctech.com/topic/721 (porozmawiaj o sposobie wykorzystania Promise)

derek
źródło
1
zakończ przypisania skrypt.src = src; Dodaj ';' do końca wszystkich zleceń
chdev77
6

Mam do tego czystą bibliotekę javascript https://github.com/robertodecurnex/J50Npi/blob/master/J50Npi.js

Przyjrzyj się temu i daj mi znać, jeśli potrzebujesz pomocy w używaniu lub zrozumieniu kodu.

Przy okazji, masz tutaj prosty przykład użycia: http://robertodecurnex.github.com/J50Npi/

robertodecurnex
źródło
6
Twoje rozwiązanie było zbyt proste dla mojego przypadku użycia, więc dodałem obsługę wielu zgłoszeń gist.github.com/1431613
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);
  });
})();

Próbka użycia:

const data = await load('http://api.github.com/orgs/kriasoft');
Konstantin Tarkus
źródło
1
Nie zapomnij, aby window[callback] = nullpozwolić funkcji na zbieranie elementów bezużytecznych.
Sukima
3

Napisałem bibliotekę, aby to załatwić tak prosto, jak to tylko możliwe. Nie ma potrzeby, aby był zewnętrzny, to tylko jedna funkcja. W przeciwieństwie do niektórych innych opcji, ten skrypt czyści się po sobie i jest uogólniony do wysyłania dalszych żądań w czasie wykonywania.

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
źródło
2

Poniżej JavaScriptprzykład wykonania JSONPpołączenia bez JQuery:

Możesz również polecić moje GitHubrepozytorium w celach informacyjnych.

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
źródło
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
źródło
Dlaczego window.document.getElementsByTagName('script')[0];a nie document.body.appendChild(…)?
Sukima
Nie powinno logAPIbyć ustawione na, nullgdy jest gotowe, aby można było na nim przeprowadzić czyszczenie pamięci?
Sukima
0

Jeśli używasz ES6 z NPM, możesz wypróbować moduł węzła „fetch-jsonp”. Fetch API Zapewnia obsługę wykonywania wywołań JsonP jako zwykłych wywołań XHR.

Warunek wstępny: powinieneś używać isomorphic-fetchmodułu node w swoim stosie.

Rajendra kumar Vankadari
źródło
0

Wklejam tylko wersję ES6 miłej odpowiedzi Sobstela:

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
źródło