JSONP 是服务器与客户端跨源通信的常用方法,最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。
它的基本思想是,网页通过添加一个 script
元素,向服务器请求 JSON 数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
缺点:
- 只能发送 get 请求,不支持 post put delete
- 不安全,xss 攻击,因此很多公司现在都不采用
实现步骤
(1)网页动态插入 script
元素,由它向跨源网址发出请求。
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute('type','text/javascript');
script.src = src;
document.body.appendChild(script);
}
window.onload = function() {
addScriptTag('http://example.com/ip?callback=foo');
}
function foo(data) {
console.log('Your public IP address is:' + data.ip);
}
通过动态添加 script
元素,向服务器 example.com
发出请求。
(2)该请求的查询字符串有一个 callback
参数,用来指定回调函数的名字,这对于 JSONP 是必需的。
(3)服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。
foo({
"ip": "8.8.8.8"
});
由于 script
元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了 foo
函数,该函数就会立即调用。作为参数的 JSON 数据被视为 JavaScript 对象,而不是字符串,因此避免了使用 JSON.parse
的步骤。
封装 JSONP 方法
参数
url
:请求地址params
:传递给后台的参数cb
:回调函数名称
代码
//=> 解构赋值
function jsonp({url, params, cb}) {
return new Promise((resolve, reject) => {
// 手动创建一个 script,插入到 body 后面
let script = document.createElement('script');
// 创建一个全局 cb 回调函数
// 数据请求回来之后,会执行这个函数,触发 resolve,完成使命,移除这个 script 标签
// 继而执行 then 中定义的回调函数
window[cb] = function (data) {
resolve(data);
document.body.removeChild(script);
}
// 拼接参数
params = {...params, cb};
let arr = [];
for (let k in params) {
arr.push(`${k}=${params[k]}`);
}
script.src = `${url}?${arr.join('&')}`;
document.body.appendChild(script);
})
}
后端服务
使用 express 框架
let express = require('express');
let app = express();
app.get('/say', function (req, res) {
let { wd, cb } = req.query;
console.log(wd);
res.end(`${cb}('中文')`);
})
app.listen(3000);