学习JSONP

利用 jsonp 进行 Javascript 的跨域数据访问

Posted by Hpyer on 2008-10-23 -14,284 Views -12 Comments

一、基础知识

  1、什么是 JSON:JavaScript Object Notation (JSON) 是一种轻量级、基于文本、语言无关的数据交换格式。它是从 ECMAScript 语言标准衍生而来的。JSON为轻便的表示结构化数据,定义了一小套格式化规则。以下是一个 JSON 对象的例子:

{
    
" Image " : {
        
" Width " : 800 ,
        
" Height " : 600 ,
        
" Title " : " View from 15th Floor " ,
        
" Thumbnail " : {
            
" Url " : " http://www.example.com/image/481989943 " ,
            
" Height " : 125 ,
            
" Width " : " 100 "
        
} ,
        
" IDs " : [ 116 , 943 , 234 , 38793 ]
    
}
}

  2、什么是 JSONP:JSONP(JSON with Padding)是一个非官方的协议,它允许在服务器端集成 Script tags 返回至客户端,通过 Javascript callback 的形式实现跨域访问(这仅仅是 JSONP 简单的实现形式)。

二、JSONP 的原理

  首先说明一下,想要使用 JSONP 来进行跨域访问,必须服务器端支持,原因后面再解释。和 AJAX 一样,JSONP 实际上也是早已存在,只是说法相对比较新颖(貌似也出来很久了,额…),而且所有做网页开发肯定都用过。为什么这么说?应为它用到的只是所有 HTML 元素中一个简单的 script 元素。看到这是不是觉得越发奇怪了?没关系,继续看下去就会茅厕(塞)顿开的,嘿嘿~来看个例子吧:
demo.html 文件

< ! DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN " " http://www.w3.org/TR/html4/loose.dtd " >
< html >
< head >
< meta http-equiv = " Content-Type " content = " text/html; charset=UTF-8 " >
< title > Demo </ title >
< script type = " text/javascript " >
function say(words) {
    alert(words);
}
</ script >
</ head >
< body >
< script type = " text/javascript " src = " demo.js " ></ script >
</ body >
</ html >

demo.js 文件

say ( " Hello, everyone! " ) ;

  运行 demo.html 文件后,是不是看到写着“Hello, everyone!”的警告框了?你可能会觉得这个例子很简单,没什么了不起的,甚至会在想:这和 JSONP 有关系吗?那么,我可以很肯定的告诉你:有关系!而且,这个例子实际上就是 JSONP 的原型!你也许会想到,JSONP 不是访问远程数据的吗?对,试想一下,如果 demo.js 文件在其它域的服务器上呢?结果会不会出现问题?我也可以很负责的告诉你:不会!你可以将上面例子中的 demo.js 更改为:http://demo.hpyer.cn/php/jsonp.php?callback=say 再试一下。
  现在,聪明的你应该已经明白 JSONP 到底是怎么回事了,那么,再来解释一下本节开头第一句话吧。看过 demo.js 文件的内容,应该知道,其只是对一个函数(通常称之为:回调函数)的调用,而需要交互的数据则通过参数形势进行返回。所以通过 JSONP 访问的服务器需要提供一个可以设置回调函数名的接口,就像 http://demo.hpyer.cn/php/jsonp.php?callback=say 中的 callback,所以,综上所述 JSONP 是需要服务器端的支持的。附上 jsonp.php 的源码:

<?php
$type = isset ( $_GET [ ' type ' ]) ? $_GET [ ' type ' ] : '' ;
$callback = isset ( $_GET [ ' callback ' ]) ? $_GET [ ' callback ' ] : '' ;
$json = '' ;
 
if ( $type == ' json ' ) {
    
$json = ' {
        "Image": {
            "Width": 800,
            "Height": 600,
            "Title": "View from 15th Floor",
            "Thumbnail": {
                "Url":  "http://www.example.com/image/481989943",
                "Height": 125,
                "Width": "100"
            },
            "IDs": [116, 943, 234, 38793]
        }
    }
' ;
} else {
    
$json = ' "Hello, everyone!" ' ;
}
 
if ( ! empty ( $callback )) {
    
$json = $callback . ' ( ' . $json . ' ) ' ;
}
 
echo $json ;
?>
三、jQuery 中的应用

  自 1.2 版本起,jQuery 加入了对 JSONP 的支持。我们可以很容易的利用 $.getJSON() 方法(或者其它基于 $.ajax() 的方法),来跨域加载 JSON 数据。来个例子吧:

< ! DOCTYPE html PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN " " http://www.w3.org/TR/html4/loose.dtd " >
< html >
< head >
< meta http-equiv = " Content-Type " content = " text/html; charset=UTF-8 " >
< title > Demo </ title >
< script type = " text/javascript " src = " jquery.js " ></ script >
< script type = " text/javascript " >
function do_jsonp() {
    $.getJSON("http://demo.hpyer.cn/php/jsonp.php?type=json&callback=?",
    function(data) {
        $('#result').val('data.Image.IDs: ' + data.Image.IDs);
    });
}
</ script >
</ head >
< body >
< a href = " javascript:do_jsonp(); " > Click me </ a >< br />
< textarea id = " result " cols = " 50 " rows = " 5 " ></ textarea >
</ body >
</ html >

  你可能注意到上面的例子中,url 被写成了 http://demo.hpyer.cn/php/jsonp.php?type=json&callback=?,需要说明的是,这个问号会被 jQuery 自动替换为回调函数的函数名(如果是一个匿名函数,jQuery 会自动生成一个带时间戳的函数名)。

四、小结

  通过本文的描述,相信你对于 JSONP 有了一个比较直观的了解,Hpyer 也相信,你也肯定可以举一反三的写出属于你自己的代码。其实,JSONP 的实现方法并不是只有回调函数一种,还有其它方式,例如将数据赋值给一个变量等。在 Web 2.0 的总体趋势之下,JS 显得越来越重要,并成了许多网页制作者所必学的一种脚本,所以,尽情的去体会 JS 带来的乐趣吧,Enjoy it!

 

=================英文资料===========================================

JSON is a lightweight data-interchange format. It was formally standardized by Douglas Crockford, and since has been received almost universally as a simple and powerful representation of data for transmission between two entities, regardless of what computer language those entities run in natively.

Cross-domain Ajax -- brief introduction

The same-origin policyin browsers dictates that certain types of data transfer in the browser layer (via JavaScript) must be restricted to only occur if the target resource's domain is identical to the page making the request. This policy is in place in all modern browsers to help protect users from unwanted or unsafe malicious JavaScript behaviors.

Cross-domain Ajax refers to the idea of making requests across domains in opposition to the same-origin restriction. However, cross-domain Ajax is not inherently unsafe or evil -- it is in fact essential to many of the world's most popular and useful mashups. But cross-domain Ajax done properly seeks to make such communication using techniques which, for various reasons, are not subject to the same-origin policy.

JSON-P (JSONP)

One such mechanism which can request content cross-domain is the <script> tag. In December 2005, Bob Ippolitoformally proposed JSONP (later dubbed JSON-P, or JSON-with-padding) as a way to leverage this property of <script> tags to be able to request data in the JSON format across domains. JSON-P works by making a <script> element (either in HTML markup or inserted into the DOM via JavaScript), which requests to a remote data service location. The response (the loaded "JavaScript" content) is the name of a function pre-defined on the requesting web page, with the parameter being passed to it being the JSON data being requested. When the script executes, the function is called and passed the JSON data, allowing the requesting page to receive and process the data.

Example:

function handle_data(data) {
   // `data` is now the object representation of the JSON data
}


---
http://some.tld/web/service?callback=handle_data:
---
handle_data({"data_1": "hello world", "data_2": ["the","sun","is","shining"]});

As you can see, the remote web service knew what function name to call based on being told the function name in the URL that made the request. As long as that function is in fact defined in the requesting page, it will be called and passed the data it was asking for.

The problem

Thus far, JSON-P has essentially just been a loose definition by convention, when in reality the browser accepts any abitrary JavaScript in the response. This means that authors who rely on JSON-P for cross-domain Ajax are in fact opening themselves up to potentially just as much mayhem as was attempted to be avoided by implementing the same-origin policy in the first place. For instance, a malicious web service could return a function call for the JSON-P portion, but slip in another set of JavaScript logic that hacks the page into sending back private user's data, etc.

JSON-P is, for that reason, seen by many as an unsafe and hacky approach to cross-domain Ajax, and for good reason. Authors must be diligent to only make such calls to remote web services that they either control or implicitly trust, so as not to subject their users to harm.

Alternatives

There are many alternatives to JSON-P, but each of them has their own drawbacks or challenges. These techniques will not be covered in detail here, except for one:CORS (aka, "Cross-Origin Resource Sharing") -- the most recent addition to browser JavaScript for making cross-domain Ajax calls. Put simply, CORS is an extension to the standardXMLHttpRequest (aka, "XHR") object, which allows the browser to make calls across domains (despite the same-origin restriction). But it does so by first "preflighting" a request to the target server to ask it for permission.

In other words, the remote server is able to opt-in or opt-out of such communication as it sees fit. For instance, a server may expose some content to Ajax requests from only a pre-defined list of approved site domains, and reject all other requests from any other pages. Or, a server may open up its content to be retrieved by any other domain, if it sees fit to do so.

At first glance, it may seem like CORS is an ideal solution for cross-domain Ajax, and makes "hacks" like JSON-P obsolete.Nicholas Zakas recently wrote about CORS for cross-domain Ajax, and gave it a glowing endorsement as the future of cross-domain Ajax in browsers.

Whether CORS as the "standard" will end up being widely implemented and relied upon for broad cross-domain Ajax usage on the web remains to be seen. There are some drawbacks, of course, as the devil is always in the details.

Firstly, CORS requires a server that implements a web-service to implement some non-trivial logic to intercept request-headers in the special "preflight" authorization requests, and to respond with properly formatted response headers depending on the server's intended policy response. If a JSON web-service needs to support cross-domain requests with JSON-P, it's a fairly straightforward change to wrap the JSON piece in a simple function call.

But asking web services across the internet to all implement lower-level logic at their web server layer to send and receive special custom headers is possibly a bit more tricky, depending on what web server software and permissions are in place. It may take several years for this technique to catch on and be to the point where the majority of web service providers is CORS-compliant. As of now, it's very new, and very few web services have done so, as opposed to tens of thousands of existing JSON-P enabled web service endpoints.

Moreover, CORS was implemented (albeit with some slight syntactic differences) only as of IE8, and not yet at all in Opera (as far as is known). So, there's a pretty decent chunk of web visitors whose browser does not support CORS, which means that a fallback for alternative cross-domain Ajax is still going to be necessary for the mid-term (1 to 3 years, perhaps).


 

The proposed solution

For now, JSON-P is a viable solution for cross-domain Ajax. While CORS may represent a more direct and less hacky way of such communication, it should probably be deployed in tandem with JSON-P techniques, so as to account for browsers and web services which do not support CORS. However, the safety concerns around JSON-P are valid and should be addressed.

So, a stricter subset definition for JSON-P is called for. The following is the proposed "standard" for only what should be considered valid, safe, allowable JSON-P.

functionName({JSON});

obj.functionName({JSON});

obj["function-name"]({JSON});

The intention is that only a single expression (function reference, or object property function reference) can be used for the function ("padding") reference of the JSON-P response, and must be immediately followed by a single ( ) enclosing pair, inside of which must be a strictly valid and parseable JSON object. The function call may optionally be followed by one ; semi-colon. No other content, other than whitespace or valid JavaScript comments, may appear in the JSON-P response, and whitespace and comments must be ignored by the browser JavaScript parser (as would normally be the case).

The most critical piece of this proposal is that browser vendors must begin to enforce this rule for script tags that are receiving JSON-P content, and throw errors (or at least stop processing) on any non-conforming JSON-P content.

In order for the browser to be able to know when it should apply such content-filtering to what might otherwise be seen as regular JavaScript content, the MIME-type "application/json-p" and/or "text/json-p" must be declared on the requesting <script> element. Furthermore, the browser can enforce that the response must be of the matching MIME-type, or fail with errors as well.

Drawbacks

It is fully known that existing browsers which don't support CORS also will not likely be updated to support this JSON-P strict enforcement, which means that users in those browsers will not be protected by their browser. However, all current browsers can add support for this content filtering, which would provide safer JSON-P for current browser users who are consuming data from web services which do not yet support CORS (or for which the author does not want to use CORS for whatever reason).

It's also recognized that this stricter definition may cause some "JSON-P" transmissions, which rely on the looser interpretation of just arbitrary JavaScript content, to fail. But this could easily be avoided by having the author (and the server) avoid referring to that content with the strictly defined JSON-P MIME-types as described above, which would then prevent the browser from selectively turning on such filtering.

Side note

One possible mitigation technique which could help users of older browsers still take advantage of JSON-P safety would be for the author to detect browsers without such support (up for discussion how this feature-test might work) and conditionally route such requests only for those unsafe browsers through a local server-proxy (or even through a client-side flash proxy likeflXHR), which could act as a gateway to the content and do the filtering logic described above.

An important side-effect of codifying a standard for JSON-P is that a parser implementation can (and will) be written to check against the strict definition. Such a parser could easily be used in the local server-proxy or the client-side flash proxy to filter unsafe/invalid content that should have been strict JSON-P.

Alternate Use Case: Unit-testing

Another interesting use-case for having a standard for JSON-P that a parser can be written to is the notion of being able to unit-test APIs which produce JSON-P output. For instance, such testing is common for JSON APIs, using something like JSONLint, but for JSON-P APIs at the moment, there's no good way to pump the output through some parser to validate it.

Moving Forward

This is a first-pass at such a definition for safe JSON-P. I hereby open up the discussion to the community at large to weigh in on the pros and cons and to collaborate together to find a workable definition that can be advocated for to the W3C, WHATWG, and browser vendors.

The best way to get involved is to write blog posts in response to this proposal, and create links to and from such discussions. In addition, this site's markup/code ishosted on github, and you can fork and modify the page to continue the discussion, and then send a pull request to have the site updated. Lastly, you can make short comments in the comment form below, but please keep your comments brief and to the point, as they will be moderated as necessary to keep the discussion on track.

 

 

=================延伸资料===========================================

Douglas Crockford

  Douglas Crockford是Web开发领域最知名的技术权威之一,ECMA JavaScript2.0标准化委员会委员。被JavaScript之父Brendan Eich称为JavaScript的大宗师(Yoda)。曾任Yahoo!资深JavaScript架构师,现任PayPal高级JavaScript架构师。
  他是JSON、JSLint、JSMin和ADSafe的创造者,也是名著《JavaScript: The Good Parts》(中文版《JavaScript语言精粹》)的作者。撰写了许多广为流传、影响深远的技术文章,包括“JavaScript:世界上最被误解的语言”。
  Douglas Crockford曾在著名的Lucasfilm电影公司任技术总监;在Paramount(派拉蒙)公司任新媒体高级总监;communities社区创始人兼CEO;State软件公司CTO。
  2012.05.14,Paypal宣布Douglas Crockford加入Paypal。
 
 
 
=================================================
https://github.com/jaubourg/jquery-jsonp/blob/master/doc/API.md

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值