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

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

108955 次浏览

我的理解是,您实际上在 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 并执行它。

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

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

看一下它,让我知道如果你需要任何帮助使用或理解代码。

顺便说一下,这里有一个简单的用法示例: http://robertodecurnex.github.com/J50Npi/

轻量级示例(支持 onSuccess 和 onTimeout)。

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

什么是 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.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);

这将加载脚本并立即调用由服务器写入/echo/打印出的 window.processJSONPResponse()。作为函数参数传入的数据现在存储在 dataFromServer局部变量中,您可以根据需要使用它。

收拾干净

一旦客户有了数据。在脚本被添加到 DOM 之后,可以立即从 DOM 中删除 script 元素:

script.parentNode.removeChild(script);
/**
* 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' );
/**
* 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');

我使用 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(讲授使用承诺的方法)

我编写了一个库来尽可能简单地处理这个问题。不需要让它外部,它只是一个功能。与其他一些选项不同,此脚本在其本身之后进行清理,并且通用于在运行时发出进一步请求。

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

如果您在 NPM 中使用 ES6,可以尝试使用节点模块“ get-jsonp”。 FetchAPI 提供对作为常规 XHR 调用进行 JsonP 调用的支持。

先决条件: 您应该在堆栈中使用 isomorphic-fetch节点模块。

请参考下面的 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);
}
}

只是粘贴了一个 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)
})
}