JSONP的起源

Posted by admin | 翻译, JavaScript, 原创 | 星期六 13 9月 2008 1:10 下午

作者:Bob Ippolito /原文链接

浏览器安全模型规定,XMLHttpRequest、框架(frame)等只能在一个域中通信。从安全角度考虑,这个规定很合理;但是,也确实给分布式(面向服务、混搭等等本周提到的概念)Web开发带来了麻烦。

为了实现跨域通信,通常的解决方案有3种:

本地代理:
需要一些硬件设施(没有服务器的客户端无法运行),并且带宽和潜伏时间也要加倍(远程服务器-代理服务器-客户端)。

Flash:
远程主机中需要部署一个crossdomain.xml文件,而且,Flash作为一门专有技术,其前途尚不明朗;换句话说,开发人员很可能要学习一种目标不确定的编程语言。

Script标签:
无法确切知道内容是否有效,没有标准的实现方法,又可能被认为是一种“安全风险”。


在此,我建议使用一种新技术,也是一种独立于标准的方法,即通过script标签来跨域获取数据,名为JSON with Padding,或者就叫JSONP。JSONP的原理很简单,但需要服务器端给予相应配合。大致来说,JSONP的实现思路就是在客户端编程时作好使用JSON数据的准备,然后再通过圆括号将这些数据括起来以创建一条有效的JavaScript语句(可能是一次有效的函数调用)。

也就是说,客户端可以使用一个用于命名jsonp的查询参数来决定可以获取的数据。最简单的情况下,如果jsonp参数为空,则返回的数据就是被括在圆括号中的JSON。

下面,我们就以del.icio.us的JSON API为例,来说明JSONP的原理。该API有一个“script tag”变量(即,可以将下面的URL作为script标签的src属性值,用以加载del.icio.us这个API提供的数据。——译者注)如下所示:

http://del.icio.us/feeds/json/bob/mochikit+interpreter:

  1. if(typeof(Delicious) == 'undefined') Delicious = {};
  2. Delicious.posts = [{
  3. "u": "http://mochikit.com/examples/interpreter/index.html",
  4. "d": "Interpreter - JavaScript Interactive Interpreter",
  5. "t": [
  6. "mochikit","webdev","tool","tools",
  7. "javascript","interactive","interpreter","repl"
  8. ]
  9. }]

如果用JSONP的方式来表示,那么与此具有相同语义的URL应该是这样的:

http://del.icio.us/feeds/json/bob/mochikit+interpreter?
jsonp=if(typeof(Delicious)%3D%3D%27undefined%27)
Delicious%3D%7B%7D%3BDelicious.posts%3D

单纯看这个URL似乎没有什么,但我们可以要求服务器在数据有效时给出通知。因此,我可以编写一个用于跟踪数据的小系统:

  1. var delicious_callbacks = {};
  2. function getDelicious(callback, url) {
  3. var uid = (new Date()).getTime();
  4. delicious_callbacks[uid] = function () {
  5. delete delicious_callbacks[uid];
  6. callback();
  7. };
  8. url += "?jsonp=" + encodeURIComponent("delicious_callbacks[" + uid + "]");
  9. // 手工输入代码,向文档中插入script标签
  10. };
  11.  
  12. getDelicious(doSomething, "http://del.icio.us/feeds/json/bob/mochikit+interpreter");

根据以上假设,用于获取数据的URL应该如下所示:
http://del.icio.us/feeds/json/bob/mochikit+interpreter?jsonp=delicious_callbacks%5B12345%5D

  1. delicious_callbacks[12345]([{
  2. "u": "http://mochikit.com/examples/interpreter/index.html",
  3. "d": "Interpreter - JavaScript Interactive Interpreter",
  4. "t": [
  5. "mochikit","webdev","tool","tools",
  6. "javascript","interactive","interpreter","repl"
  7. ]
  8. }])

可见,由于使用圆括号括住了返回的数据,这就相当于把一个JSONP请求转化成了一次函数调用,或者得到了一个纯粹的JSON直接量。服务器所要配合做的,就是在JSON数据的开头添加一小段文本(即回调函数的名称。——译者注)并将JSON数据放在括号中!

当然,接下来最好是使用Mochikit、Dojo等框架来抽象JSONP,从而让自己省去动手编写DOM以插入script标签的麻烦。

没错,JSONP只是解决了标准化的问题。假如远程主机想通过script标签向页面中注入恶意代码,而不是返回JSON数据,那么页面安全可能会随时受到威胁。不过,一旦实现了JSONP,那么对开发人员来说肯定是一件省时省力的大好事,在此基础上各种一般化的抽象、教程及文档也会应运而生的。

注:缩写词 JSONP 由 Bob Ippolito 在一篇名为 “Remote JSON - JSONP” 的文章中提出。但许多支持以 JSONP 技术实现跨域通信的厂商没有称其为 JSONP。例如,雅虎公司就称这种技术为 “JSON with callbacks”。另外,原文发表于2005年12月5日。

8 条评论 »

  1. 评论 作者 hello cuckoo — 2008-09-16 @ 9:42 上午

    中秋好。
    怎么把我的链接去掉了呢?
    给我做个链接可以吗???
    非常感谢!!
    我的EMAIL:hardcometure@163.com

  2. 评论 作者 vampire — 2008-09-16 @ 8:41 下午

    大概是说通过script的src来实现跨域,通过经编码的json在url中传递数据?
    是不是说页面上的js生产带有不同参数的jsonp,通过src传递给服务端,服务端根据该jsonp返回相应数据?有点不太明白,能否提供一个实例?谢谢

  3. 评论 作者 admin — 2008-09-17 @ 8:43 下午

    @vampire

    你的理解是对的,看来我的翻译你能看明白,呵呵。使用JSONP技术时,一般是由JS在客户端页面中动态插入script标签,将其src属性设置为带参数的URL。当页面加载时,前述URL会将参数通过GET请求发送到相应服务器端应用程序(由URL表示),服务器根据参数返回数据——注意,这个数据格式是JSON,并且(注意)被包含在一个函数调用中,例如:callback({json_data})。这样,在客户端页面中存在预定义的callback(data)函数的定义时,服务器返回的函数调用就会立即执行,由客户端的函数对数据进行操作。

    实际的例子有很多,如Yahoo Search、Google Base、Flickr Search、Amazon Search等等。要学习和使用这些站点提供的支持JSONP的API,一般要注意两点:一是有的站点要求注册密钥(使用时必须放到参数中),二是要注意参数的使用方法。例如,有的API要求使用预定义的回调函数,而有的API则允许使用者自己定义回调函数。

    下面就是向Flickr Search请求JSONP响应的URL,其中使用了预定义的回调函数jsonFlickrApi(参数中不必给出):

    http://api.flickr.com/services/rest/
    ?method=flickr.photos.search
    &api_key=85618ad7d326d8ef93c6bee9ed32706f
    &per_page=5&format=json&text=china

    下面这个URL发送到Google Base,它允许开发人员使用自己定义的回调函数:

    http://www.google.com/base/feeds/snippets
    ?q=jquery
    &alt=json-in-script
    &callback=customCallback

    把前面的URL放到浏览器地址栏中,回车,即可看到结果。

  4. 评论 作者 hello cuckoo — 2008-09-17 @ 8:51 下午

    管理员能回复下吗?
    不可以的话也说声。
    有劳了。
    :)

  5. 评论 作者 admin — 2008-09-17 @ 8:58 下午

    给你回的邮件没收到吗?最近,网站空间提供商要求我们必须删除可能侵犯知识产权的链接。所以,就全都删除了。

  6. 评论 作者 vampire — 2008-09-19 @ 12:08 上午

    @admin:嗯 明白了 实际上跟本地调用函数差不多 只不过通过src提交了请求并得到了具体数据参数 很聪明的办法 非常感谢

    去年就开始看你的博客了 只是来的很少 会继续关注你的博客:-)

  7. 评论 作者 hqlulu — 2008-10-13 @ 9:23 上午

    function customCallback(obj){
    for(var i in obj)
    alert(i+”:”+obj[i]);
    }

    应该是类似这样

  8. 评论 作者 vampire — 2008-10-26 @ 4:53 下午

    Javascript 使用 CSS 异步跨域获取数据:
    http://www.gracecode.com/Archive/Display/2469

对这篇文章的评论的 RSS 聚合。 TrackBack URI

发表您的评论

验证码  If you cannot see the CheckCode image,please refresh the page again!