title | date |
---|---|
JSONP详解 |
2017/02/21 14:47:11 |
JSONP(JSON with Padding)是资料格式 JSON 的一种“使用模式”,可以让网页从别的网域要资料。另一个解决这个问题的新方法是跨来源资源共享。(参考:https://zh.wikipedia.org/wiki/JSONP)
- 曾经的Ajax不能跨域请求(现在的也不能,不过有cors)
- Web上使用script调用js文件不存在跨域问题(实际上,只要拥有src属性的标签都允许跨域,比如script,img,iframe)
- 那个时候,想要通过web端跨域访问数据,只可以在服务器端设法把数据装进js,然后客户端调用
- 刚好这个时候JSON大行其道
- 所以,解决方案就出来,web端像调用脚本一样来跨域请求服务器上动态生成的js文件
- 为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP。
通过JSONP的起源,我们大概也知道了JSONP就是为了跨域资源访问的。
我们知道,在script标签中请求的js代码,到客户端之后,是能被自动执行的。
我们先构造一个后端(采用node实现):
var http = require('http');
var server = http.createServer((req, res) => {
var sendObj = {
url: req.url,
name: 'test'
};
res.write(`callback(${JSON.stringify(sendObj)})`);
res.end();
});
server.listen(9999, () => {
console.log('started.')
});
我们要使用这个这个数据呢?可以用Ajax,可能会产生跨域问题
另外,可以用如下写法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP TEST</title>
</head>
<body>
<script>
function callback(obj){
console.log(obj);
}
</script>
<script src="http://localhost:9999/abc"></script>
</body>
</html>
打开这个页面后,我们会看到控制台会输出一个对象Object {url: "/abc", name: "test"}
,
也就是后端返回的对象。
当使用script请求地址时,会将返回的字符串,默认当成js解析。由于后端返回是的callback(xxx),所以会调用本地的callback函数。
从原理上来看,要使用JSONP,必须要后端返回相应的数据,这个就是JSONP的模式了,允许客户端传递一个callback函数,后端将数据包裹在callback函数中返回。
从原理也能看出,JSONP并不要求必须传递JSON格式的数据,只要是JS函数能够认可的数据都是可以传递的
知道了原理,我们很容易能够实现一个jsonp的函数调用,代码如下:
window.JSONP = function(url, callback){
callback = callback || 'callback';
var result;
return new Promise((resolve, reject) => {
var overwritten;
var scriptEl = document.createElement('script');
scriptEl.src = url + '?callback=' + callback;
//加载完成后,删除callback
scriptEl.onload = function(){
if(overwritten === undefined){
delete window[callback];
}else{
window[callback] = overwritten;
}
resolve(result);
}
//挂载一个callback到window上
overwritten = window[callback]; //先保存一个,用完之后再还原
window[callback] = function(data){
result = data
}
document.head.appendChild(scriptEl);
});
};
如何用?
window.JSONP('http://localhost:9999/abc').then((data) => {
console.log(data);
});
在jQuery中,我们使用jsonp感觉就和使用ajax没有区别,但实际上它们的底层实现实现是完全不一样的,毕竟原理都不同。
虽然很多库和框架都把jsonp封装到了ajax中,但是一定要记得jsonp不是ajax的一个特例。
当前,除了用jsonp跨域之外,还可以采用服务端代理(通过不跨域的后端程序,发送webClient去请求数据,然后转发),CORS(API服务器允许跨域的一种设置)。