杂谈-JSONP探索

1,147 阅读4分钟

前言

平时工作当中,也已经很久没有用JSONP 来跨域请求了,现重新整理一下,写出自己对 JSONP 的理解。如果写的有不对的,请各位看官评论指出,感谢。

行文时,系统环境为 macOS Mojave v10.14.5,开发工具为 vscode 1.45.1,浏览器为 Goolge Chrome 版本 81.0.4044.138 。

JSONP是什么

JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。

这是引用了百度百科对 JSONP 的定义。笔者自己的理解是, JSONP 是一种利用浏览器 script 标签的能力实现访问跨域数据的小技巧。就是利用 script 标签能访问跨域资源的能力,利用 JSON 去填充返回内容,以达到跨域的目的。

JSONP解决了什么问题

上文说到了,JSONP 解决了跨域问题。那啥是跨域呢,简单说就是发起请求的网站与被请求的网站,它俩的协议、主机、端口不是完全一样的。表现就是,跨域时,请求是不会成功的。
JSONP 到底是怎么实现的呢,笔者的理解是,基于下面三点实现的:

  1. 浏览器的 script 标签不受同源策略的限制,可以请求到其它源的资源。
  2. script 标签请求的资源拿到后,会被浏览器的 JS引擎 执行。
  3. script 请求到的资源是在 全局上下文 里执行的。

JSONP测试

接下来就写点代码测试一下,并用大白话把过程描述一下。
用koa模拟服务端,代码如下

const koa = require("koa");
const mount = require("koa-mount");
const app = new koa();
app.use(
  mount("/jsonp", (ctx) => {
    let callback = ctx.query.abc;
    let javascriptFileText = `
            console.log(1);
			${callback}("这是服务端的内容");
        `;
    ctx.body = javascriptFileText;
  })
);
app.listen(8123);

前端代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>client</title>
</head>
<body>
    <script>
        function handler(res) {
            console.log(res)
        }
        window.addEventListener('load', () => {
            var url = 'http://127.0.0.1:8123/jsonp?abc=handler'
            var script = document.createElement('script');
            script.src = url
          	// 把script标签塞入到文档里,就会发起请求
            document.getElementsByTagName('head')[0].appendChild(script);
        })

    </script>
</body>
</html>

笔者认为的过程如下:

  1. 服务端接收到 /jsonp这个路由的请求时(当然了,是get请求),就返回一段文本,内容是一个 console.log(1) 和一个函数调用,在这个调用里传入 "这是服务端的内容" 。对于服务端来说,这一段始终是字符串文本。
  2. _前端在把 script标签塞入到文档后,这个 script 就开始请求,请求拿到后是 console.log(1);handler("这是服务端的内容") 这样的内容,然后浏览器 JS引擎 就把这段内容执行,可以对应 eval() 理解。执行的时候,是处于 全局上下文 的。所以是能拿到 handler 这个函数的。然后呢,在 handler 里就通过res拿到了服务端传回给前端的内容。是不是很妙?!!我第一次用的时候,真的是眼前一亮,原来可以这么玩。
  3. 我这里取名为 abc 是为了说明, JSONP 的使用不是说一定要写成 xx/xx/xx?callback=xxx 的格式的,如果你和后台同学约定好了函数名字,你甚至可以不用传callback,直接写成 xx/xx/xx 的接口请求就行了,功能是一定能实现的,只是可能你会被同事打而已。并且, handler 方法里也不一定要传 JSON 格式的内容,你也可以写成 handler(this) ,然后前端页面里拿到的就是 handler 函数执行上下文里的 this 变量了,同样,这样写没有任何意义,只会被打而已。也可以写成 handler('name="服务端给的name"&age="服务端给的age"') ,这样,通过 & 和 = 去切割获取内容,这样也很麻烦呀,所以最终大家都选择传 JSON 格式的内容,这是最方便获取内容的方式。所以就叫 JSONP 。(这样看来,是不是也可以叫 callbackP 、 STRINGP 呢??)

后语

JSONP 这种技巧,现在(2020-06-01)已经很少有听到有还在使用的。因为现代浏览器基本都支持了 CORS 标准,这可比只支持 GET 的 JSONP 强太多了。以后,笔者估计自己是不会用到 JSONP 了,这篇文章只是笔者看到有些公司在面试时,还是会问 JSONP 的内容。所以就记录一下。