前几天写了一篇关于ajax跨域的问题,总结了常见的几种容易混淆的问题,顺带讲了些原理。今天这篇主要是说明 ajax 跨域常用的一种办法 jsonp 的原理和具体实现,代码存放在 github 上 js-cross-origin, 使用了 hapi 这个 nodejs 框架。感兴趣的可以 star 和 watch,我这几天有空会持续更新代码。@H_502_1@
jsonp 详解
为了叙述方便,我们把ajax的发出方,我们浏览器上的加载好的网页的域称为源域,将ajax要访问的远端api所在的域称为目标域,这两个域不同。由于浏览器的同源策略的,源域中的ajax访问目标域api,会触发跨域访问错误。@H_502_1@
@H_502_1@
No 'Access-Control-Allow-Origin' header is present on the requested resource.
幸运的是,script标签目标源的js文件并不会有问题。所以聪明的前端工程师想出了一个办法。@H_502_1@
网页端插入一个script标签,src指向目标api 的 url(只能是 get api,因为 script 加载 js 文件是 http get 方法)。这里做一个小改动,url后面加上 query,?callback=handle@H_502_1@
后端 api 处理函数接收到请求,发现有 callback 参数,则将参数值拿下来,得到 handle@H_502_1@
后端用 handle 包装数据,返回给浏览器,注意,返回的 content-type 必须是 text/javascript; charset=utf-8@H_502_1@
-
handle(data)
浏览器发现内容是 js(查看 content-type),则调用js解释器执行 handle(data)@H_502_1@
至此,jsonp 流程完成。@H_502_1@
常见的几种问题
为什么 jsonp 只能访问后台 GET API?
jsonp 利用 script 标签加载 js 脚本不受同源策略的影响这个特性,绕过跨域限制。因为 script 加载 js 脚本使用的是 http get 方法,所以 jsonp 也只能访问 GET API。 因此,后台其他类型的 API 也要改造成 GET 类型。@H_502_1@
我使用 xx 语言/框架,怎么做才能支持 jsonp ?
一般成熟的 web 框架都会具备 jsonp 支持,通过简单的配置,可以完成 jsonp 对服务端的以下几个要求。@H_502_1@
读取 callback 名,比如 handle@H_502_1@
将数据封装在 handle 中@H_502_1@
将封装好的内容作为 js 脚本内容返回,注意 content-type 必须为 text/javascript; charset=utf-8 以便浏览器正常解析执行 js@H_502_1@
server.route({ method: 'GET',path:'/users',config: { jsonp: 'callback',},handler: function (request,reply) { const jsonp = request.query.jsonp; const users = [ { id: '1',name: 'Ada'},{ id: '2',name: 'Bob' } ]; if (jsonp) { return reply(`${jsonp}(${users})`); //hapi会自动将结果的content-type设置为 text/javascript; charset=utf-8 } else { return reply(users); // 默认 content-type 是 application/json } } });
如果运气不好框架刚好不支持,那也不必担心,按照上面的要求,前后端约定好,实现 jsonp 并不难。@H_502_1@
征集问题
如果有其他关于 jsonp 的问题,欢迎留言,我会将典型的问题和解答,追加到上面。@H_502_1@