Як зробити запит JSONP з Javascript без JQuery?


122

Чи можна зробити запит JSONP між доменами в JavaScript без використання jQuery або іншої зовнішньої бібліотеки? Я хотів би використовувати сам JavaScript, а потім проаналізувати дані та зробити їх об’єктом, щоб я міг ним користуватися. Чи потрібно використовувати зовнішню бібліотеку? Якщо ні, то як це зробити?


Відповіді:


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
Ось JSBin, який можна використати, щоб поспілкуватися з JSONP з Вікіпедії. На це посилалось у цій відповіді .
rkagerer

1
Я думаю, що варто зазначити, що відповідь має бути такої форми: foo(payload_of_json_data)ідея полягає в тому, що коли він завантажується в тег скрипту, він викликає функцію foo з корисним навантаженням вже як об'єкт javascript і не потрібен розбір.
Восьминіг

@WillMunn Я не думаю, що це можливо з JSONP. Це злом від днів до CORS. Для чого потрібно встановити заголовки? Сервер спеціально повинен приймати запити JSONP, тому він повинен бути налаштований на серверне обслуговування.
Метт Бал

Ви маєте рацію, я неправильно прочитав документацію api, є спеціальний параметр запиту, щоб зробити те, що я хотів, використовуючи jsonp appologies.
Буде Мунн

@WillMunn не хвилюйся. Радий, що ви змогли це розібратися!
Метт Бал

37

Легкий приклад (із підтримкою onSuccess та onTimeout). Вам потрібно передати ім'я зворотного дзвінка в межах 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;
})();

Використання зразка:

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

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


29

Що таке JSONP?

Важливо пам'ятати про jsonp, що це насправді не протокол або тип даних. Це просто спосіб завантаження сценарію на льоту та обробки сценарію, який вводиться на сторінку. У дусі JSONP це означає введення нового об’єкта javascript з сервера в клієнтську програму / скрипт.

Коли потрібен JSONP?

Це 1 метод надання асинхронному доступу домену / обробці даних з іншого на одній сторінці на одному домені. Перш за все, він використовується для перегляду обмежень CORS (Cross Origin Resource Sharing), які виникатимуть із запитом XHR (ajax). Навантаження сценарію не підпадає під обмеження CORS.

Як це робиться

Введення нового об’єкта javascript із сервера може бути реалізовано багатьма способами, але найпоширенішою практикою є те, щоб сервер реалізував виконання функції "зворотного дзвінка" з необхідним об'єктом, переданим у нього. Функція зворотного дзвінка - це лише функція, яку ви вже налаштували на клієнті, сценарій якого ви завантажуєте дзвінки, у момент завантаження сценарію для обробки переданих йому даних.

Приклад:

У мене є додаток, який реєструє всі предмети в будинку когось. Моя програма налаштована, і тепер я хочу отримати всі предмети в головній спальні.

Моя заявка ввімкнена app.home.com. Апис, з якого мені потрібно завантажити дані, увімкнено api.home.com.

Якщо сервер явно не встановлений для його дозволу, я не можу використовувати ajax для завантаження цих даних, оскільки навіть на сторінках окремих піддоменів застосовуються обмеження XHR CORS.

В ідеалі налаштовуйте речі, щоб дозволити X-домен XHR

В ідеалі, оскільки api та додаток перебувають у одному домені, я можу мати доступ для налаштування заголовків api.home.com. Якщо це зробити, я можу додати елемент Access-Control-Allow-Origin: заголовка, що надає доступ app.home.com. Якщо припустити, що заголовок налаштований так: Access-Control-Allow-Origin: "http://app.home.com"це набагато безпечніше, ніж налаштування JSONP. Це тому, що app.home.comможна отримати все, що хоче, api.home.comне api.home.comнадаючи CORS доступ до всього Інтернету.

Наведене вище рішення XHR неможливо. Налаштування JSONP На моєму клієнтському сценарії: я налаштовував функцію для обробки відгуку з сервера під час здійснення дзвінка JSONP. :

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

Потрібно налаштувати сервер, щоб повернути міні-скрипт, схожий на те, що "processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"він може бути розроблений для повернення такої рядки, якщо щось подібне //api.home.com?getdata=room&room=main_bedroomвикликається.

Потім клієнт встановлює тег сценарію як такий:

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

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

Це завантажує сценарій і негайно викликає window.processJSONPResponse()сервер як написаний / ехо / роздрукований. Дані, передані як параметр функції, тепер зберігаються в dataFromServerлокальній змінній, і ви можете робити з нею все, що вам потрібно.

Прибирати

Після того, як клієнт має дані, тобто. одразу після додавання сценарію до DOM, елемент сценарію можна видалити з DOM:

script.parentNode.removeChild(script);

2
Дуже дякую, це мені дуже допомогло в моєму проекті. Незначна проблема: я отримав SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data. Після додавання до даних єдиних лапок, все спрацювало нормально, тому:"processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"
Хайн ван Дайк

17

Я розумію, що ви фактично використовуєте теги сценаріїв з JSONP, так ...

Перший крок - створити свою функцію, яка буде обробляти JSON:

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

Переконайтеся, що ця функція доступна на глобальному рівні.

Далі, додайте елемент сценарію до DOM:

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

Сценарій завантажить JavaScript, який будує постачальник API, і виконає його.


2
Дякую всім. Зробив це зйомка через Інтернет в пошуках деяких даних, а потім я щось роблю з цим. Я використав eval () на дані відповідей, які допомогли мені впоратися з об'єктом - масив, що це (я думаю). {Це був ведмідь, який розбирався в розборі з моєю обмеженою мозковою силою, але я, нарешті, отримав цінність з нього}. Фантастичний.
Дейв

@ Dave @ Matt Може бути , я буду нечітким на JSONP, але вам не потрібно evalабо parseабо що - небудь. Ви повинні отримати JavaScript, який браузер може просто виконати, правда?
sdleihssirhc

Моє погано, вибачте за плутанину. Спроба вивести річ (значення? Властивість?) З масиву змушувала мою голову крутитися (Вкладення, зворотний виклик, масив, елемент, об’єкт, рядок, значення, фігурні дужки, дужки ...). Я видалив своє використання eval і досі отримав властивість (value?) З масиву (object? Element?), Який я хотів.
Дейв

10

як я використовую jsonp, як показано нижче:

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

то використовуйте такий метод "jsonp":

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

довідка:

JavaScript XMLHttpRequest за допомогою JsonP

http://www.w3ctech.com/topic/721 (говорити про спосіб використання Обіцяти)


1
скасувати призначення script.src = src; додати ';' до кінця всіх завдань
chdev77

6

У мене є чиста бібліотека JavaScript для цього https://github.com/robertodecurnex/J50Npi/blob/master/J50Npi.js

Погляньте на це і дайте мені знати, чи вам потрібна допомога щодо використання чи розуміння коду.

До речі, тут є простий приклад використання: http://robertodecurnex.github.com/J50Npi/


6
Ваше рішення було занадто простим для мого використання, тому я додав підтримку декількох запитів gist.github.com/1431613
Чад 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);
  });
})();

Зразок використання:

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

1
Не забудьте window[callback] = nullдозволити функцію збирати сміття.
Сукіма

3

Я написав бібліотеку, щоб якомога простіше впоратися з цим. Не потрібно робити це зовнішнім, його лише одна функція. На відміну від деяких інших варіантів, цей скрипт очищається після себе і узагальнюється для подачі подальших запитів під час виконання.

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

Нижче наведено JavaScriptприклад для здійснення JSONPдзвінка без JQuery:

Також ви можете направити моє GitHubсховище для довідок.

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

Чому window.document.getElementsByTagName('script')[0];і ні document.body.appendChild(…)?
Сукіма

Чи не logAPIслід встановлювати, nullколи це робиться, щоб на ньому можна було проводити збір сміття?
Сукіма

0

Якщо ви використовуєте ES6 з NPM, ви можете спробувати модуль вузла "fetch-jsonp". Fetch API надає підтримку для здійснення дзвінка JsonP як звичайного виклику XHR.

Необхідна умова: ви повинні використовувати isomorphic-fetchмодуль вузла у своєму стеці.


0

Щойно вставляємо ES6-версію приємної відповіді:

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)
    })
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.