Bagaimana cara membuat permintaan JSONP dari Javascript tanpa JQuery?

122

Bisakah saya membuat permintaan JSONP lintas domain di JavaScript tanpa menggunakan jQuery atau pustaka eksternal lainnya? Saya ingin menggunakan JavaScript itu sendiri dan kemudian mengurai data dan menjadikannya objek sehingga saya dapat menggunakannya. Apakah saya harus menggunakan perpustakaan eksternal? Jika tidak, bagaimana saya bisa melakukannya?

Dave
sumber

Jawaban:

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
sumber
2
Berikut JSBin yang dapat digunakan untuk mengutak-atik JSONP dari Wikipedia. Itu direferensikan dalam jawaban ini .
rkagerer
1
Saya pikir ada baiknya menunjukkan bahwa responsnya harus dalam bentuk foo(payload_of_json_data):, idenya adalah bahwa ketika dimuat ke tag skrip, itu memanggil fungsi foo dengan muatan sudah sebagai objek javascript dan tidak perlu parsing.
Gurita
@WillMunn Saya tidak berpikir itu bisa dilakukan dengan JSONP. Ini adalah retasan dari hari-hari sebelum CORS. Untuk apa Anda perlu menyetel header? Server secara khusus perlu menerima permintaan JSONP sehingga harus disiapkan untuk melayani dengan cara yang wajar.
Matt Ball
Anda benar, saya salah membaca beberapa dokumentasi api, ada parameter kueri khusus untuk melakukan apa yang saya inginkan saat menggunakan aplikasi jsonp.
Will Munn
@Willunn jangan khawatir. Senang Anda bisa menyelesaikannya!
Matt Ball
37

Contoh ringan (dengan dukungan untuk onSuccess dan onTimeout). Anda harus memasukkan nama panggilan balik dalam URL jika Anda membutuhkannya.

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

Penggunaan sampel:

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

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

sobstel
sumber
29

Apa itu JSONP?

Hal penting yang harus diingat dengan jsonp adalah bahwa ini sebenarnya bukan protokol atau tipe data. Ini hanya cara memuat skrip dengan cepat dan memproses skrip yang diperkenalkan ke halaman. Dalam semangat JSONP, ini berarti memperkenalkan objek javascript baru dari server ke dalam aplikasi / skrip klien.

Kapan JSONP dibutuhkan?

Ini adalah metode 1 yang memungkinkan satu domain untuk mengakses / memproses data dari yang lain di halaman yang sama secara asinkron. Terutama, ini digunakan untuk mengganti pembatasan CORS (Cross Origin Resource Sharing) yang akan terjadi dengan permintaan XHR (ajax). Pemuatan skrip tidak tunduk pada batasan CORS.

Bagaimana melakukannya

Memperkenalkan objek javascript baru dari server dapat diimplementasikan dengan banyak cara, tetapi praktik yang paling umum adalah server mengimplementasikan eksekusi fungsi 'callback', dengan objek yang diperlukan diteruskan ke dalamnya. Fungsi panggilan balik hanyalah sebuah fungsi yang telah Anda siapkan pada klien di mana skrip yang Anda muat panggilannya pada saat skrip dimuat untuk memproses data yang diteruskan ke dalamnya.

Contoh:

Saya memiliki aplikasi yang mencatat semua item di rumah seseorang. Aplikasi saya sudah diatur dan sekarang saya ingin mengambil semua item di kamar tidur utama.

Aplikasi saya aktif app.home.com. Api yang saya perlukan untuk memuat data sedang aktif api.home.com.

Kecuali jika server secara eksplisit diatur untuk mengizinkannya, saya tidak dapat menggunakan ajax untuk memuat data ini, karena bahkan halaman di subdomain terpisah tunduk pada batasan XHR CORS.

Idealnya, atur semuanya untuk mengizinkan x-domain XHR

Idealnya, karena api dan aplikasi berada di domain yang sama, saya mungkin memiliki akses untuk menyiapkan header api.home.com. Jika saya melakukannya, saya dapat menambahkan Access-Control-Allow-Origin: item header yang memberikan akses ke app.home.com. Dengan asumsi tajuk diatur sebagai berikut Access-Control-Allow-Origin: "http://app.home.com":, ini jauh lebih aman daripada menyiapkan JSONP. Ini karena app.home.combisa mendapatkan semua yang diinginkan api.home.comtanpa api.home.commemberikan akses CORS ke seluruh internet.

Solusi XHR di atas tidak memungkinkan. Siapkan JSONP Pada skrip klien saya: Saya menyiapkan fungsi untuk memproses tanggapan dari server ketika saya melakukan panggilan JSONP. :

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

Server perlu diatur untuk mengembalikan skrip mini yang tampak seperti "processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"Ini mungkin dirancang untuk mengembalikan string seperti //api.home.com?getdata=room&room=main_bedroomitu jika sesuatu seperti itu dipanggil.

Kemudian klien menyiapkan tag skrip seperti:

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

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

Ini memuat skrip dan segera memanggil window.processJSONPResponse()sebagai ditulis / echo / dicetak oleh server. Data yang diteruskan sebagai parameter ke fungsi sekarang disimpan di dataFromServervariabel lokal dan Anda dapat melakukannya dengan apa pun yang Anda butuhkan.

Membersihkan

Setelah klien memiliki datanya, mis. segera setelah skrip ditambahkan ke DOM, elemen skrip dapat dihapus dari DOM:

script.parentNode.removeChild(script);
embun
sumber
2
Terima kasih banyak, ini sangat membantu saya dengan proyek saya. Masalah kecil: Saya telah menerima SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data. Setelah menambahkan tanda kutip tunggal ke data, semuanya bekerja dengan baik, jadi:"processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"
Hein van Dyke
17

Pemahaman saya adalah bahwa Anda benar-benar menggunakan tag skrip dengan JSONP, sooo ...

Langkah pertama adalah membuat fungsi Anda yang akan menangani JSON:

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

Pastikan bahwa fungsi ini dapat diakses di tingkat global.

Selanjutnya, tambahkan elemen skrip ke DOM:

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

Skrip akan memuat JavaScript yang dibuat oleh penyedia API, dan menjalankannya.

sdleihssirhc.dll
sumber
2
Terimakasih semuanya. Saya mendapatkannya melalui internet untuk mencari beberapa data dan kemudian saya melakukan sesuatu dengannya. Saya menggunakan eval () pada data respons yang membantu saya menangani objek - array itu (menurut saya). {Itu adalah seekor beruang yang sedang memikirkan parsing dengan kekuatan otak saya yang terbatas tapi akhirnya saya mendapatkan nilai darinya}. Fantastis.
Dave
@ Dave @ Matt Mungkin aku berada kabur pada JSONP, tetapi Anda tidak perlu evalatau parseatau apapun. Anda harus mendapatkan JavaScript yang dapat dijalankan oleh browser, bukan?
sdleihssirhc
Saya buruk, maaf atas kebingungannya. Mencoba mengeluarkan benda (nilai? Properti?) Dari larik membuat kepalaku berputar (Penyarangan, panggilan balik, larik, elemen, objek, string, nilai, tanda kurung kurawal, tanda kurung ...). Saya menghapus penggunaan eval dan masih mendapatkan properti (nilai?) Dari array (objek? Elemen?) Yang saya inginkan.
Dave
10

cara saya menggunakan jsonp seperti di bawah ini:

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

kemudian gunakan metode 'jsonp' seperti ini:

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

referensi:

JavaScript XMLHttpRequest menggunakan JsonP

http://www.w3ctech.com/topic/721 (berbicara tentang cara penggunaan Promise)

derek
sumber
1
hentikan tugas script.src = src; Tambahkan ';' ke akhir semua tugas
chdev77
6

Saya memiliki perpustakaan javascript murni untuk melakukan itu https://github.com/robertodecurnex/J50Npi/blob/master/J50Npi.js

Lihat dan beri tahu saya jika Anda memerlukan bantuan dalam menggunakan atau memahami kode.

Btw, Anda memiliki contoh penggunaan sederhana di sini: http://robertodecurnex.github.com/J50Npi/

robertodecurnex
sumber
6
Solusi Anda agak terlalu sederhana untuk kasus penggunaan saya, jadi saya menambahkan beberapa permintaan dukungan 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);
  });
})();

Contoh penggunaan:

const data = await load('http://api.github.com/orgs/kriasoft');
Konstantin Tarkus
sumber
1
Jangan lupa untuk window[callback] = nullmengizinkan fungsinya menjadi sampah dikumpulkan.
Sukima
3

Saya menulis perpustakaan untuk menangani ini, sesederhana mungkin. Tidak perlu dibuat eksternal, fungsinya hanya satu. Tidak seperti beberapa opsi lain, skrip ini membersihkan dirinya sendiri, dan digeneralisasikan untuk membuat permintaan lebih lanjut pada waktu proses.

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

Temukan JavaScriptcontoh di bawah ini untuk melakukan JSONPpanggilan tanpa JQuery:

Juga, Anda dapat merujuk GitHubrepositori saya untuk referensi.

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
sumber
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
sumber
Kenapa window.document.getElementsByTagName('script')[0];tidak document.body.appendChild(…)?
Sukima
Tidak logAPIharus disetel ke nullsaat selesai sehingga pengumpulan sampah dapat dilakukan di sana?
Sukima
0

Jika Anda menggunakan ES6 dengan NPM, Anda dapat mencoba modul node "fetch-jsonp". Fetch API Memberikan dukungan untuk membuat panggilan JsonP sebagai panggilan XHR biasa.

Prasyarat: Anda harus menggunakan isomorphic-fetchmodul node di tumpukan Anda.

Rajendra kumar Vankadari
sumber
0

Hanya menempelkan versi ES6 dari jawaban bagus 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
sumber