1

前几天写了一篇关于ajax跨域的问题,总结了常见的几种容易混淆的问题,顺带讲了些原理。今天这篇主要是说明 ajax 跨域常用的一种办法 jsonp 的原理和具体实现,代码存放在 github 上 js-cross-origin, 使用了 hapi 这个 nodejs 框架。感兴趣的可以 star 和 watch,我这几天有空会持续更新代码。

jsonp 详解

为了叙述方便,我们把ajax的发出方,我们浏览器上的加载好的网页的域称为源域,将ajax要访问的远端api所在的域称为目标域,这两个域不同。由于浏览器的同源策略的,源域中的ajax访问目标域api,会触发跨域访问错误。

clipboard.png

No 'Access-Control-Allow-Origin' header is present on the requested resource.

幸运的是,script标签目标源的js文件并不会有问题。所以聪明的前端工程师想出了一个办法。

  • 网页端插入一个script标签,src指向目标api 的 url(只能是 get api,因为 script 加载 js 文件是 http get 方法)。这里做一个小改动,url后面加上 query,?callback=handle

  • 后端 api 处理函数接收到请求,发现有 callback 参数,则将参数值拿下来,得到 handle

  • 后端用 handle 包装数据,返回给浏览器,注意,返回的 content-type 必须是 text/javascript; charset=utf-8

  • 网页端 script 内容加载完成

    handle(data)

    浏览器发现内容是 js(查看 content-type),则调用js解释器执行 handle(data)

至此,jsonp 流程完成。

常见的几种问题

为什么 jsonp 只能访问后台 GET API?

jsonp 利用 script 标签加载 js 脚本不受同源策略的影响这个特性,绕过跨域限制。因为 script 加载 js 脚本使用的是 http get 方法,所以 jsonp 也只能访问 GET API。 因此,后台其他类型的 API 也要改造成 GET 类型。

我使用 xx 语言/框架,怎么做才能支持 jsonp ?

一般成熟的 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 的问题,欢迎留言,我会将典型的问题和解答,追加到上面。


驽马
1.3k 声望26 粉丝

前端/android/入门级设计


引用和评论

1 篇内容引用
0 条评论