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

How to make a JSONP request from Javascript without JQuery?

提问人:Dave 提问时间:5/26/2011 最后编辑:informatik01Dave 更新时间:3/20/2023 访问量:110790

问:

我可以在不使用 jQuery 或其他外部库的情况下在 JavaScript 中发出跨域 JSONP 请求吗?我想使用JavaScript本身,然后解析数据并使其成为一个对象,以便我可以使用它。我必须使用外部库吗?如果没有,我该怎么做?

JavaScript JSONP

评论

0赞 Matt Ball 5/26/2011
在 JAVASCRIPT 中制作和处理 jsonp 请求的帮助的可能重复

答:

17赞 sdleihssirhc 5/26/2011 #1

我的理解是,您实际上将脚本标签与JSONP一起使用,因此...

第一步是创建将处理 JSON 的函数:

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

确保此函数可在全局级别访问。

接下来,向 DOM 添加一个 script 元素:

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

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

评论

2赞 Dave 5/26/2011
谢谢大家。让它在互联网上搜索一些数据,然后我用它做一些事情。我在响应数据上使用 eval(),这有助于我处理对象 - 数组(我认为)。{这是一只熊用我有限的脑力弄清楚解析,但我终于从中提取了价值}。匪夷所思。
0赞 sdleihssirhc 5/27/2011
@Dave @Matt 也许我对 JSONP 很模糊,但你不应该需要 OR 或任何东西。你应该得到浏览器可以执行的 JavaScript,对吧?evalparse
0赞 Dave 5/29/2011
我的错,对不起,造成混乱。试图从数组中获取东西(值?属性?)让我头晕目眩(嵌套、回调、数组、元素、对象、字符串、值、大括号、括号......我删除了对 eval 的使用,但仍然从数组(object?element?)我想要。
155赞 Matt Ball 5/26/2011 #2
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赞 rkagerer 3/8/2013
这里有一个JSBIN,可以用来摆弄维基百科上的JSONP这个答案中引用了它。
1赞 Octopus 9/19/2014
我认为值得指出的是,响应的形式应该是: ,这个想法是,当它加载到脚本标签中时,它会调用 foo 函数,其有效负载已经作为 javascript 对象,并且不需要解析。foo(payload_of_json_data)
0赞 Matt Ball 8/15/2016
@WillMunn 我认为这在 JSONP 中是行不通的。这是 CORS 之前的黑客攻击。您需要为什么设置标题?服务器特别需要接受 JSONP 请求,因此应将其设置为以合理的方式提供服务。
0赞 Will Munn 8/15/2016
你是对的,我误读了一些 api 文档,在使用 jsonp appologies 时有一个特殊的查询参数可以做我想做的事。
0赞 Matt Ball 8/16/2016
@WillMunn不用担心。很高兴你能够解决这个问题!
5赞 Roberto Decurnex 5/27/2011 #3

我有一个纯 javascript 库来做到这一点 https://github.com/robertodecurnex/J50Npi/blob/master/J50Npi.js

看一看,如果你在使用或理解代码时需要任何帮助,请告诉我。

顺便说一句,您这里有简单的使用示例:http://robertodecurnex.github.com/J50Npi/

评论

6赞 Chad Cache 12/5/2011
您的解决方案对于我的用例来说有点太简单了,所以我添加了多个请求支持 gist.github.com/1431613
38赞 sobstel 10/24/2012 #4

轻量级示例(支持 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

评论

1赞 gidiwe2427 1/3/2022
你应该在完成后删除假 JS
33赞 dewd 2/1/2015 #5

什么是 JSONP?

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

什么时候需要 JSONP?

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

它是如何完成的

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

例:

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

我的应用程序已开启。我需要从中加载数据的 API 是 。app.home.comapi.home.com

除非服务器明确设置为允许它,否则我无法使用 ajax 加载此数据,因为即使是单独子域上的页面也受 XHR CORS 限制的约束。

理想情况下,将内容设置为允许 x 域 XHR

理想情况下,由于 api 和应用程序位于同一域中,因此我可能有权在 上设置标头。如果我这样做,我可以添加一个标题项,授予对 .假设标头的设置如下: ,这比设置 JSONP 安全得多。这是因为可以从中获取它想要的一切,而无需让 CORS 访问整个互联网。api.home.comAccess-Control-Allow-Origin: app.home.comAccess-Control-Allow-Origin: "http://app.home.com"app.home.comapi.home.comapi.home.com

上述 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);

评论

2赞 Hein van Dyke 6/8/2017
非常感谢,这对我的项目有很大帮助。一个小问题:我收到了 .在数据中添加单引号后,一切正常,因此:SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data"processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"
0赞 Chris Ferdinandi 4/16/2015 #6
/**
 * 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' );

评论

0赞 Sukima 8/5/2016
为什么而不是?window.document.getElementsByTagName('script')[0];document.body.appendChild(…)
0赞 Sukima 8/5/2016
是否应设置为何时完成,以便可以对其执行垃圾回收?logAPInull
4赞 Konstantin Tarkus 6/30/2015 #7
/**
 * 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);
      window[callback] = null;
      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赞 Sukima 8/5/2016
不要忘记允许对函数进行垃圾回收。window[callback] = null
16赞 derek 7/22/2015 #8

我使用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赞 chdev77 6/15/2017
终止赋值 script.src = src;将“;”添加到所有分配的末尾
3赞 Fresheyeball 3/13/2016 #9

我写了一个库来尽可能简单地处理这个问题。无需将其外部化,它只是一个功能。与其他一些选项不同,此脚本会自行清理,并通用化为在运行时发出进一步的请求。

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);
                };
        });
}
-1赞 Rajendra kumar Vankadari 3/19/2018 #10

如果你将 ES6 与 NPM 一起使用,你可以尝试节点模块 “fetch-jsonp”。 Fetch API 支持将 JsonP 调用作为常规 XHR 调用进行。

先决条件: 您应该在堆栈中使用 Node Module。isomorphic-fetch

3赞 Mayur Shedage 5/29/2018 #11

请找到下面的 JavaScript 示例,以便在没有 jQuery 的情况下进行 JSONP 调用:

另外,您可以参考我的 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赞 just_user 6/28/2019 #12

只是粘贴 sobstel 的 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)
    })
}