Как сделать 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 08

1
Я думаю, стоит отметить, что ответ должен иметь форму:, foo(payload_of_json_data)идея заключается в том, что когда он загружается в тег script, он вызывает функцию foo с полезной нагрузкой уже как объект javascript, и синтаксический анализ не требуется.
— Octopus

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

вы правы, я неправильно прочитал некоторую документацию по api, есть специальный параметр запроса, чтобы делать то, что я хотел, при использовании приложений jsonp.
— Уилл Манн,

@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-интерфейсы, с которых мне нужно загружать данные, включены api.home.com.

Если сервер явно не настроен для этого, я не могу использовать ajax для загрузки этих данных, так как даже страницы на отдельных поддоменах подпадают под ограничения XHR CORS.

В идеале настройте все, чтобы разрешить XHR домена x

В идеале, поскольку 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"]}');"
— Hein van Dyke

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 и по-прежнему получил свойство (значение?) Из массива (объект? Элемент?), Которое я хотел.
— Дэйв

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 (расскажите о способе использования Promise)


1
завершить сценарий назначений. src = src; добавить ';' до конца всех заданий
— chdev77

6

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

Взгляните на него и дайте мне знать, если вам понадобится помощь в использовании или понимании кода.

Кстати, у вас есть простой пример использования: http://robertodecurnex.github.com/J50Npi/


6
Ваше решение было слишком простым для моего варианта
— 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);
  });
})();

Пример использования:

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

1
Не забудьте window[callback] = nullразрешить сборку мусора.
— Sukima

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(…)?
— Sukima

Не logAPIследует устанавливать, nullкогда это будет сделано, чтобы можно было выполнить сборку мусора?
— Sukima

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.