JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,用于在浏览器和服务器之间交换信息。JSONP(JSON With Padding),就是打包在函数调用中的的JSON(或者包裹的JSON)。笼统地讲,JSON是一种数据格式,JSONP是一种数据调用方式。

1、起因:同源策略

Ajax允许在不干扰Web应用程序的显示和行为的情况下在后台进行数据请求,但是,浏览器的“同源策略”限制了发起异步请求的函数XMLHttpRequest的使用域,也就是说,ajax请求只能获取和当前页面处在同一个“域”的服务器页面的数据,如果要跨域请求数据,浏览器会阻止相关的行为,“同源策略”这一浏览器策略已经具有很久的历史,最早可以追溯到Netscape Navigator 2.0版本的浏览器中。

2、可引入跨域脚本的script标签

与ajax请求不同,HTML中的script可以在当前域引入并执行“第三方源”的javascript脚本,例如通过script标签引入CDN中的jquery脚本:

<script src="//cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>;

显然,上述标签中引入的 jquery.min.js 文件与当前页面肯定不在同一服务器上,但是浏览器却没有组织 script 标签从其它域请求数据。

利用 script 标签可以跨域请求的特性,如果WEB应用需要从第三方源发起特定的ajax请求来获取json数据的时候,可以使用js动态地在DOM中插入一个script标签,将需要请求的ajax地址“伪装”成script标签的src属性,从而通过script标签来获取跨域的json数据,这就是jsonp大致的实现原理。

3、问题

假如某个web应用需要从url地址为 http://ajax.remote.com/service.php 的第三方服务器页面上获取json数据,远程页面server.php的作用是产生一段json数据:{"result":"success"},实现代码可以是:

$array = array("result"=>"success");
echo json_encode($array);

在本地html中使用js来模拟实现jsonp跨域请求:

<script type="text/javascript">
var JSONP = document.createElement("script");
JSONP.type = "text/javascript";
JSONP.src = "http://ajax.remote.com/service.php";
document.getElementsByTagName("head")[0].appendChild(JSONP);
</script>

按照之前所述,浏览器应该正常获取json数据,但是,实际运行代码可以发现浏览器没有正确执行代码。出现问题的原因在于:本质上script请求道远程的脚本数据后,会直接在本地页面中执行获取到的js脚本数据,但是demo中远程获取到的json数据{"result":"success"}并不是真正意义上的javascript脚本,浏览器也就不能将json数据直接解析执行。

4、jsonpCallback回调函数

为了解决这一问题,jsonp请求通常会在发起远程请求的时候传递一个jsonpCallback参数(名称可以自己定义),该参数的值则作为请求到的json数据读取函数,具体的函数含义在本地html页面中进行定义,这样客户端获取到的json数据就能当做js脚本来直接提供给浏览器执行。

为了在本地html中正确读取demo中请求到的json数据,在发起jsonp请求之前先定义一个readjson函数来读取请求的json数据,而函数名称“readjson”则应当作为参数jsonpCallback(名称不一定唯一)的值一起发送到远程页面中,代码:

<script type="text/javascript">
function readjson(json) {
    alert("request json result is:" + json.result);
}

var JSONP=document.createElement("script");
JSONP.type="text/javascript";
JSONP.src="http://ajax.remote.com/service.php?jsonpCallback=readjson";
document.getElementsByTagName("head")[0].appendChild(JSONP);
</script>

同样,为了响应客户端发起的jsonp请求,远程页面server.php也应该支持自定义回调函数,实现代码可以是:

$jsonpCallback=htmlspecialchars($_REQUEST['jsonpCallback']);
$array=array("result"=>"success");
echo $jsonpCallback. '(' .json_encode($array). ')';

通过上述代码,客户端其实最终请求到的数据是readjson({"result":"success"}),由于客户端页面中已经预先定义了readjson函数的含义(即通过alert弹出json中的result值),因此这段数据可以被浏览器直接当成js脚本来执行,客户端也就完成了一次正确的jsonp跨域请求。

5、jQuery发起jsonp请求

使用jQuery发起jsonp请求十分方便,通常使用jQuery提供的三个方法:$.ajax$.get$.getJSON,使用$.ajax发起jsonp请求代码例如:

<script type="text/javascript">
$.ajax({
    url:"http://ajax.remote.com/service.php",
    dataType:'jsonp',
    data:'',
    jsonp:'jsonpCallback',
    success:function(data) {
        alert("request json result is:" + data.result);
    }
});
</script>