一、JSONP 是什么?
1.1 概念
JSONP(JSON with Padding)是资料格式 JSON 的一种“使用模式”,可以让网页从别的网域要资料。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server2.example.com 的服务器沟通,而 HTML 的 <script>
元素是一个例外。
利用 <script>
元素的开源策略,从其他域 动态的获取 数据,这就是JSONP。(让使用者利用 script 元素注入的方式绕开同源策略)
1.1.1 思路
- 一个返回JSON数据的URL接口
- JavaScript 底层会用 XMLHttpRequest 跟这个 URL接口 请求数据
- 为了让浏览器可以在 <script> 元素执行,返回的数据必须是可执行的 JavaScript脚本
1.2 JSONP前缀
在 JSONP 的使用模式里,该 URL 回传的是由函数呼叫包起来的动态生成 JSON,这就是JSONP 的“填充(padding)”或是“前辍(prefix)”的由来。
惯例上浏览器提供回调函数的名称当作送至服务器的请求中命名查询参数的一部份,例如:
<script type="text/javascript" src="http://server2.example.com/RetrieveUser?UserId=1823&jsonp=parseResponse"> </script>
服务器会在传给浏览器前将 JSON 数据填充到回调函数(parseResponse)中。
浏览器得到的回应已不是单纯的资料叙述而是一个脚本。
在本例中,浏览器得到的是:
parseResponse({"Name": "Cheeso", "Id" : 1823, "Rank": 7})
这个前缀可以是
- 某个回调函数
- 变量赋值
- 其他JavaScript脚本
JSONP 要求(也就是使用 JSONP 模式的请求)的回应不是 JSON 也不被当作 JSON 解析——回传内容可以是任意的运算式,甚至不需要有任何的 JSON,不过惯例上填充部份还是会触发函数调用的一小段 JavaScript 片段,而这个函数呼叫是作用在 JSON 格式的资料上的。
1.3 JSONP 的安全问题
使用远端网站的 script 标签会让远端网站得以注入任何的内容至网站里。如果远端的网站有 JavaScript 注入漏洞,原来的网站也会受到影响.现在有一个正在进行计划在定义所谓的 JSON-P 严格安全子集,使浏览器可以对 MIME 类别是“application/json-p”请求做强制处理。如果回应不能被解析为严格的 JSON-P,浏览器可以丢出一个错误或忽略整个回应。
粗略的 JSONP 部署很容易受到跨网站的伪造要求(CSRF/XSRF)的攻击。因为 HTML <script> 标签在浏览器里不遵守同源策略,恶意网页可以要求并取得属于其他网站的 JSON 资料。当使用者正登入那个其他网站时,上述状况使得该恶意网站得以在恶意网站的环境下操作该 JSON 资料,可能泄漏使用者的密码或是其他敏感资料。
1.4 JSONP DEMO
1.4.1 跨域js文件中的代码(当然指符合web脚本安全策略的),web页面也是可以无条件执行的。
远程服务器remoteserver.com根目录下有个remote.js文件代码如下:
alert(‘跨域调用成功‘);
本地服务器localserver.com下有个jsonp.html页面代码如下:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript"src="http://remoteserver.com/remote.js"></script> </head> <body> </body> </html>
页面会弹出一个 弹出框,显示跨域调用成功。
1.4.2 现在 在 jsonp.html页面定义一个函数,然后在远程remote.js中传入数据进行调用。
jsonp.html页面代码如下:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript"> varlocalHandler=function(data){ alert(‘我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:‘+data.result); }; </script> <script type="text/javascript"src="http://remoteserver.com/remote.js"></script> </head> <body> </body> </html>
remote.js文件代码如下:
localHandler({“result”:”我是远程js带来的数据”});
运行之后查看结果,页面成功弹出提示窗口,显示本地函数被跨域的远程js调用成功,并且还接收到了远程js带来的数据。
跨域远程获取数据的目的基本实现了,但是出现了一个问题? 怎么让远程的JS 站点它 应该调用的本地函数名呢?
1.4.3 服务端返回的JS脚本,是动态生成的
调用者把参数传到服务端,然后服务端生成相对应的脚本,并返回。
看jsonp.html页面的代码:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript"> // 得到航班信息查询结果后的回调函数 varflightHandler=function(data){ alert(‘你查询的航班结果是:票价 ‘+data.price+‘ 元,‘+‘余票 ‘+data.tickets+‘ 张。‘); }; // 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码) varurl="http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler"; // 创建script标签,设置其属性 varscript=document.createElement(‘script‘); script.setAttribute(‘src‘,url); // 把script标签加入head,此时调用开始 document.getElementsByTagName(‘head‘)[0].appendChild(script); </script> </head> <body> </body> </html>
调用者传了一个code参数到服务端,告诉服务端要查 CA1998 航班的信息,而callback 参数告诉服务器,本地回调函数名叫 flightHandler。
OK,服务器很聪明,这个叫做flightResult.aspx的页面生成了一段这样的代码提供给jsonp.html(服务端的实现这里就不演示了,与你选用的语言无关,说到底就是拼接字符串):
flightHandler({ "code":"CA1998", "price":1780, "tickets":5 });
二、jQuery对JSONP的实现(目的:便于与用户界面交互,方便调用,提高开发效率)
依然沿用上面那个航班信息查询的例子,假定返回jsonp结果不变:
<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>UntitledPage</title> <script type="text/javascript"src=jquery.min.js"></script> <script type="text/javascript"> jQuery(document).ready(function(){ $.ajax({ type:"get", async:false, url:"http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998", dataType:"jsonp", jsonp:"callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback) jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据 success:function(json){ alert(‘您查询到航班信息:票价: ‘+json.price+‘ 元,余票: ‘+json.tickets+‘ 张。‘); }, error:function(){ alert(‘fail‘); } }); }); </script> </head> <body> </body> </html>
为什么没有写flightHandler函数?竟然运行成功?
这就是jQuery的功劳了,jquery在处理jsonp类型的ajax时自动生成回调函数并把数据取出来供success属性方法来调用。
Ajax 与 JSONP 的异同
- ajax和jsonp这两种技术在调用方式上“看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装;
- 但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。
- 所以说,其实ajax与jsonp的区别不在于是否跨域,ajax通过服务端代理一样可以实现跨域,jsonp本身也不排斥同域的数据的获取。
- 还有就是,jsonp是一种方式或者说非强制性协议,如同ajax一样,它也不一定非要用json格式来传递数据,如果你愿意,字符串都行,只不过这样不利于用jsonp提供公开服务。
总而言之,jsonp不是ajax的一个特例,哪怕jquery等巨头把jsonp封装进了ajax,也不能改变着一点!
参考文档: