如何在没有JQuery的情况下从Javascript发出JSONP请求?


Answers:


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,可用于摆弄 Wikipedia的JSONP该答案中引用了
rkagerer 2013年

1
我认为值得指出的是,响应应采用以下形式:foo(payload_of_json_data),即当将其加载到script标签中时,它将调用foo函数,并且其有效负载已经作为javascript对象,因此无需解析。
Octopus

@WillMunn我认为这与JSONP无关。这是CORS之前的时代的骇客。您需要设置什么标题?服务器特别需要接受JSONP请求,因此应将其设置为以合理的方式提供服务。
马特·鲍尔

没错,我误读了一些api文档,有一个特殊的查询参数可以执行我在使用jsonp应用程序时想要的操作。
Will Munn

@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


28

什么是JSONP?

使用jsonp时要记住的重要一点是,它实际上不是协议或数据类型。它只是一种动态加载脚本并处理引入页面的脚本的方法。本着JSONP的精神,这意味着将新的javascript对象从服务器引入到客户端应用程序/脚本中。

何时需要JSONP?

这是一种允许一个域异步访问/处理同一页面中另一个域的数据的一种方法。首先,它用于覆盖XHR(ajax)请求可能发生的CORS(跨源资源共享)限制。脚本加载不受CORS限制。

怎么做

从服务器引入新的javascript对象可以通过多种方式实现,但是最常见的做法是让服务器通过将所需的对象传递到其中来实现“回调”功能的执行。回调函数只是您已经在客户端上设置的函数,您加载的脚本在脚本加载时调用函数以处理传递给它的数据。

例:

我有一个将所有物品记录在某人家中的应用程序。我的应用程序已设置好,现在我要检索主卧室中的所有物品。

我的申请已开启app.home.com。我需要从中加载数据的API处于开启状态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.comCORS访问整个Internet的情况下获得所需的一切。

上面的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元素:

script.parentNode.removeChild(script);

1
非常感谢,这对我的项目非常有帮助。一个小问题:我已经收到了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);

该脚本将加载API提供程序生成的JavaScript,并执行它。


2
感谢大家。在互联网上搜索了一些数据,然后对其进行了处理。我在响应数据上使用了eval(),这有助于我处理对象(我认为)是数组。{这是一只熊,我的脑力有限,无法解决问题,但我终于从中受益了}。太棒了
戴夫

@戴夫@马特也许我对JSONP模糊,但你不应该需要evalparse什么的。您应该获得浏览器可以执行的JavaScript,对吗?
sdleihssirhc,2011年

我不好,对不起您的困惑。试图从数组中获取东西(值?属性?)使我头晕(嵌套,回调,数组,元素,对象,字符串,值,大括号,方括号...)。我删除了对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);
});

参考:

使用JsonP的JavaScript XMLHttpRequest

http://www.w3ctech.com/topic/721(谈论Promise的使用方式)


1
终止任务脚本。src = src; 添加“;” 至所有作业的结尾
chdev77


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版本的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)
    })
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.