今天面试被问到“听说过 JSONP 吗?”,虽然名字很熟悉,却不记得在哪听过用过,于是回家立马研究一下是什么,本文就是研究后的小结。
曾经想做一个“豆瓣FM”的第三方客户端,用的是 node-webkit,即可以用 HTML+CSS+JS 写桌面客户端。网上已经有人分析出了 豆瓣FM 的 API,理论上只需要乖乖调用便可。但当我把其中用到的页面在浏览器打开的时候,却什么都加载不出来。
后来查看 console 才发现原来 JQuery 的 $.get()
是不支持跨域访问的。那什么什么是跨域访问呢?
用前面的例子,假如我做了一个网页,最终放在了自己的域名lotabout.me 下,那么这个网页里的 JS 代码,如果试图访问除了域名 ‘lotabout.me’ 外的其它域名的内容,就称为跨域访问。
传统上,浏览器的实现禁止 XMLHttpRequest 请求跨域访问,因为这样会引发安全问题。
具体会引发什么安全问题,可以参考 Stackoverflow: Why the cross domain ajax is a security concern 。
这里举一个例子: 跨站请求伪造 。如下图:
即当你打开一个网站(例如 baidx.com)时,网站上可以嵌入一些 Javascript 代码,在你没有注意到的时候向银行的网站发送请求,而你之前登陆过银行的网站,所以银行的服务器就认为是你在做操作,所以在你不注意的时候可能就向别人转帐了。
现在防止跨站请求伪造的方法是银行在自己的页面里嵌入一个随机数,用户正常访问时连同这个随机数一起提交,而伪造的请求得不到这个随机数,因此银行就不响应它。但如果允许的跨域访问,’baidx.com’ 网页上的脚本就可以向银行另外发出请求,得到一个新的随机数,从而绕过随机数这个保护措施。
总而言之,跨域访问并不是为了阻挡开发者,而是为了保证安全。
我们知道,HTML 中的 <script>
标签是可以访问别的域名的,JSONP 就是利用 <script>
标签进行跨域访问的,那么具体是如何做到的呢?(下例修改自 wikipedia )
考虑我们通过 XMLHttpRequest 向服务器发送请求: http://server.example.com/Users/1234
,将用户 ID:1234 作为参数传给服务器,服务器返回一个 JSON 对象,如下:
{
"Name": "Foo",
"Id": 1234,
"Rank": 7
}
但是浏览器拒绝了这个跨域请求,所以我们要另寻它法,方法就是利用 <script>
标签,如下:
<script type="application/javascript"
src="http://server.example.com/Users/1234">
</script>
上述这段代码会从服务器 http://server.example.com/Users/1234
上获取相应的内容,并将它作为 Javascript 执行。
然而我们知道,服务器返回的是一个 JSON 格式的数据,如果把这段代码作为 Javascript 执行则浏览器会报语法错误 SyntaxError: missing ; before statement
。因此,这个技巧想要成功的话,还需要目标服务器帮忙。
如果目标服务器对返回的 JSON 数据进行一下包装:
parseResponse({"Name": "Foo", "Id": 1234, "Rank": 7});
那么我们只要事先定义好 parseResponse
函数,就可以对返回的数据进行处理了:
综上,JSONP 有如下要点:
<script>
标签允许跨域访问的特点。 还有其它一些解决跨域访问需求的方法:
启用 JSONP 也会引发其它的安全问题,可以参考 wikipedia:JSONP 。