1 min read

JSONP

Ajax 请求如果返回JSON数据,我们可以用JSON.parse析出,又或者用jQuery提供的parseJSON。这个概念很容易理解。但名称与它接近的JSONP概念却有些复杂。起码,情况要比返回JSON数据复杂、难理解些。

JSONP的由来

之所以会有JSONP出现,是因为浏览器的同源策略限制 – 我们无法跨域请求数据。但我们又有跨域请求数据的需求,那么我们就需要一个变通方法。既然JavaScript脚本不受同源策略的限制,于是它就被应用到跨域请求数据的实践中。

JSONP的概念

JSONP的全文是JSON with padding,表示一串JSON数据用函数名称包装起来。

举个例子:

{"firstName":"Sam","lastName":"Chen","gender":"male"}

上面是一串JSON数据,假设它放在github.com/chenxsan域上的human.json文件里,因为它与我的博客不同源,所以我并无法从我的博客发Ajax请求,要求github说返回该文件然后解析。但我可以把该数据包装在一个JavaScript函数里,保存为human.js文件:

human({"firstName":"Sam","lastName":"Chen","gender":"male"});

这其实就是一个函数传入一个对象并执行的代码。

而我在我博客页面可预先定义一个同名函数:

function human(data) {
    console.log(data.firstName + data.lastName + "is" + data.gender);
}

之后在博客页面通过script标签动态调用github.com域上的human.js文件,这就类似于在页面插入如下语句:

<script src = "https://raw.github.com/chenxsan/blog/master/human.js"></script>

这样,通过在博客页面上定义函数,并且调用不同域上的JS传入数据执行了函数,我得到JSON数据 – 查看浏览器的控制台信息,我可以看到字符串’Sam Chen is male’。

上面的说明中,包含JSON数据的文件是保存为human.js的,实际上,src并不介意它是什么文件格式,我甚至可以把它保存为.html或者其他格式,照样可以用,没有影响。在实际应用中,服务器端语言可以自由定义请求的URL – 前端页面只关心服务器端能不能返回相应格式的数据。

jQuery与JSONP

jQuery中,JSONP的实现与上面描述的差别不大。jQuery通过在URL附加?callback=?这样的参数提交请求给服务器(参数名称是客户端和服务器共同协定的,不必要叫callback,比如接下来的例子中,回调名用的是jsoncallback。

例子:

$.getJSON('http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?',function(data){
    console.log(data.title);
    console.log(typeof data);
});

demo如下,请按console中的run按钮:

测试JSONP

用浏览器开发者工具查看请求的URL:

jsonp 请求 flickr 数据

我们能看到,我们请求的URL地址中最后一个问号被替换成jQuery20206085280561819673_1384954063431&_=1384954063432。这是jQuery处理的结果,不需要我们打理。

HTTP 返回的数据则是下面这样:

jQuery20206085280561819673_1384954063431&_=1384954063432({……});

还有些情况下,需要特定的回调名称,则可以设置 jsonpCallback

$.ajax({
    url: 'http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?', 
    dataType: "jsonp",
    jsonpCallback: "_preloadCallback",
    success: function(data) {
        alert(data.title);
    }
});

既然有了定义好的回调函数,又有了远程取回的带数据的执行函数,我们就得到不同域的JSON数据。

参考

  1. 使用 JSONP 实现跨域通信
  2. flickr 公開的 Feed
报告问题 修订

如果你有自建 https 代理的需求,欢迎尝试 Phantom,一键搭建,方便快捷。查看 demo