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

sobstel
источник
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);
dewd
источник
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, и выполнит его.

sdleihssirhc
источник
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)

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

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

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

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

robertodecurnex
источник
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);
                };
        });
}
Fresheyeball
источник
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)
    })
}
just_user
источник