前端更优雅的使用 jsonp
背景:最近项目中又使用到了 jsonp 这一项跨域的技术,(主要还是受同源策略影响),下面有为大家提供封装好的函数及对应使用示例,欢迎大家阅读理解
同源策略的介绍
同源策略是由 Netscape 公司提出的安全策略,现在所有支持 JavaScript 的浏览器都会使用这个策略。
所谓同源是指域名、协议、端口都相同。以 http://www.xxxxx.com:80/ 为例,http:// 为协议,域名是 www.xxxxx.com,端口是80。
为了安全,浏览器不允许进行跨域请求。当我们通过 Ajax 在网页和服务器之间发送或接收数据时,需要保证网页与所请求的地址是同源的,否则无法请求成功。同源策略可以防止 JavaScript 脚本从您的网站中读取数据,并将数据发送到其它的网站。如果没有同源策略,很有可能会有恶意的程序泄露您网站中的内容。
虽然同源策略在一定程度上提高了网站的安全,但也会给开发带来一些麻烦,例如在访问一些开发接口时,由于同源策略的存在,会调用失败。要解决这种问题就需要用到跨域,跨域的方法有许多种,其中最经典的就是 JSONP。
JSONP 介绍
JSONP 全称“JSON with Padding”,译为“带回调的 JSON”,它是 JSON 的一种使用模式。通过 JSONP 可以绕过浏览器的同源策略,进行跨域请求。
-
在进行 Ajax 请求时,由于同源策略的影响,不能进行跨域请求,而 script 标签的 src 属性却可以加载跨域的 JavaScript 脚本,JSONP 就是利用这一特性实现的。
-
与 Ajax 的区别:在使用 JSONP 进行跨域请求时,服务器不再返回 JSON 格式的数据,而是返回一段调用某个函数的 JavaScript 代码,在 src 属性中调用,来实现跨域。
-
优点:JSONP 兼容性好,在一些老旧的浏览器种也可以运行。
-
缺点:只能进行 GET 请求。
JSONP 的使用
前端的 jsonp 异步函数封装
思路:
- 每调用一次 jsonp,生成唯一的 callback 回调函数 key,注册在 window 对象。
- 后端收到请求后,返回一个以 callback 参数作为函数名的函数的调用和一系列参数。
- 响应完成,处理完数据后,自动的移除掉 script 标签,window 注册的回调函数。
// 序列化参数
function serializeParams(params: { [x: string]: string | number | boolean }) {
return Object.keys(params)
.map(
(key) => encodeURIComponent(key) + "=" + encodeURIComponent(params[key])
)
.join("&");
}
function jsonp(url: string, params = {}) {
// 生成唯一的回调函数名
const callbackName: string = `jsonp_${Date.now()}`;
const script = document.createElement("script");
script.src = `${url}?${serializeParams(params)}&callback=${callbackName}`;
document.body.appendChild(script);
return new Promise((resolve, reject) => {
// 注册全局回调函数
window[callbackName] = (data: unknown) => {
resolve(data);
// 删除 script 标签
document.body.removeChild(script);
// 删除全局回调函数
delete window[callbackName];
};
// 错误处理
script.onerror = () => {
reject(new Error("JSONP request failed."));
// 删除 script 标签
document.body.removeChild(script);
// 删除全局回调函数
delete window[callbackName];
};
});
}
前端 jsonp 使用示例:
- 服务端对应处理下面 ↓
const getUserInfo = async () => {
let apiRoot = "http://localhost:3000";
let userInfo = await jsonp(`${apiRoot}/userInfo`, { name: "小名" });
console.log(userInfo);
//拿到数据处理逻辑即可
};
getUserInfo();
前端执行收到响应结果如下:
服务端的逻辑示例
思路:
- 收到前端的请求后,根据参数信息返回调用前端函数名和一系列参数的 json 字符串的响应格式。
- 该示例使用了 node express 框架,同学自行了解
let express = require("express"); //node 框架
let url = require("url");
let app = express();
app.get("/userInfo", function (req, res) {
//参数解析
let urlParams = url.parse(req.url, true);
console.log(urlParams, "url参数");
if (urlParams.query.callback) {
let userInfoRes = `${urlParams.query.callback} (${JSON.stringify({
name: urlParams.query.name ?? "张三",
age: 21,
})})`;
console.log(userInfoRes, "jsonp 响应结果");
res.end(userInfoRes);
} else {
res.end("404");
}
});
app.listen(3000);
后端收到请求结果如下:
总结:上述代码中
- jsonp 的使用一定是前后端的紧密配合,文档的模糊也会导致后期维护艰难。
-
有疑问的同学可以私信我、对帮助到同学欢迎大家点赞、收藏评论。