前几天写了一篇关于ajax跨域的问题,总结了常见的几种容易混淆的问题,顺带讲了些原理。今天这篇主要是说明 ajax 跨域经常使用的一种办法 jsonp 的原理和具体实现,代码存放在 github 上 js-cross-origin, 使用了 hapi 这个 nodejs 框架。感兴趣的能够 star 和 watch,我这几天有空会持续更新代码。javascript
为了叙述方便,咱们把ajax的发出方,咱们浏览器上的加载好的网页的域称为源域,将ajax要访问的远端api所在的域称为目标域,这两个域不一样。因为浏览器的同源策略的,源域中的ajax访问目标域api,会触发跨域访问错误。前端
No 'Access-Control-Allow-Origin' header is present on the requested resource.
幸运的是,script标签目标源的js文件并不会有问题。因此聪明的前端工程师想出了一个办法。java
网页端插入一个script标签,src指向目标api 的 url(只能是 get api,由于 script 加载 js 文件是 http get 方法)。这里作一个小改动,url后面加上 query,?callback=handlenode
后端 api 处理函数接收到请求,发现有 callback 参数,则将参数值拿下来,获得 handlegit
后端用 handle 包装数据,返回给浏览器,注意,返回的 content-type 必须是 text/javascript; charset=utf-8github
网页端 script 内容加载完成web
handle(data)
浏览器发现内容是 js(查看 content-type),则调用js解释器执行 handle(data)ajax
至此,jsonp 流程完成。json
jsonp 利用 script 标签加载 js 脚本不受同源策略的影响这个特性,绕过跨域限制。由于 script 加载 js 脚本使用的是 http get 方法,因此 jsonp 也只能访问 GET API。 所以,后台其余类型的 API 也要改形成 GET 类型。segmentfault
通常成熟的 web 框架都会具有 jsonp 支持,经过简单的配置,能够完成 jsonp 对服务端的如下几个要求。
读取 callback 名,好比 handle
将数据封装在 handle 中
将封装好的内容做为 js 脚本内容返回,注意 content-type 必须为 text/javascript; charset=utf-8 以便浏览器正常解析执行 js
好比个人代码中,服务器端的配置。
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 并不难。
若是有其余关于 jsonp 的问题,欢迎留言,我会将典型的问题和解答,追加到上面。