瀏覽器安全模型規(guī)定,XMLHttpRequest、框架(frame)等只能在一個域中通信。從安全角度考慮,這個規(guī)定很合理;但是,也確實給分布式(面向服務(wù)、混搭等等本周提到的概念)Web開發(fā)帶來了麻煩。
為了實現(xiàn)跨域通信,通常的解決方案有3種:
本地代理:
需要一些硬件設(shè)施(沒有服務(wù)器的客戶端無法運行),并且?guī)捄蜐摲鼤r間也要加倍(遠程服務(wù)器-代理服務(wù)器-客戶端)。
Flash:
遠程主機中需要部署一個crossdomain.xml文件,而且,F(xiàn)lash作為一門專有技術(shù),其前途尚不明朗;換句話說,開發(fā)人員很可能要學習一種目標不確定的編程語言。
Script標簽:
無法確切知道內(nèi)容是否有效,沒有標準的實現(xiàn)方法,又可能被認為是一種“安全風險”。
在此,我建議使用一種新技術(shù),也是一種獨立于標準的方法,即通過script標簽來跨域獲取數(shù)據(jù),名為JSON with Padding,或者就叫JSONP。JSONP的原理很簡單,但需要服務(wù)器端給予相應配合。大致來說,JSONP的實現(xiàn)思路就是在客戶端編程時作好使用JSON數(shù)據(jù)的準備,然后再通過圓括號將這些數(shù)據(jù)括起來以創(chuàng)建一條有效的JavaScript語句(可能是一次有效的函數(shù)調(diào)用)。
也就是說,客戶端可以使用一個用于命名jsonp的查詢參數(shù)來決定可以獲取的數(shù)據(jù)。最簡單的情況下,如果jsonp參數(shù)為空,則返回的數(shù)據(jù)就是被括在圓括號中的JSON。
下面,我們就以del.icio.us的JSON API為例,來說明JSONP的原理。該API有一個“script tag”變量(即,可以將下面的URL作為script標簽的src屬性值,用以加載del.icio.us這個API提供的數(shù)據(jù)。——譯者注)如下所示:
http://del.icio.us/feeds/json/bob/mochikit+interpreter:
- if(typeof(Delicious) == 'undefined') Delicious = {};
- Delicious.posts = [{
- "u": "http://mochikit.com/examples/interpreter/index.html",
- "d": "Interpreter - JavaScript Interactive Interpreter",
- "t": [
- "mochikit","webdev","tool","tools",
- "javascript","interactive","interpreter","repl"
- ]
- }]
如果用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似乎沒有什么,但我們可以要求服務(wù)器在數(shù)據(jù)有效時給出通知。因此,我可以編寫一個用于跟蹤數(shù)據(jù)的小系統(tǒng):
- var delicious_callbacks = {};
- function getDelicious(callback, url) {
- var uid = (new Date()).getTime();
- delicious_callbacks[uid] = function () {
- delete delicious_callbacks[uid];
- callback();
- };
- url += "?jsonp=" + encodeURIComponent("delicious_callbacks[" + uid + "]");
- // 手工輸入代碼,向文檔中插入script標簽
- };
- getDelicious(doSomething, "http://del.icio.us/feeds/json/bob/mochikit+interpreter");
根據(jù)以上假設(shè),用于獲取數(shù)據(jù)的URL應該如下所示:
http://del.icio.us/feeds/json/bob/mochikit+interpreter?jsonp=delicious_callbacks%5B12345%5D
- delicious_callbacks[12345]([{
- "u": "http://mochikit.com/examples/interpreter/index.html",
- "d": "Interpreter - JavaScript Interactive Interpreter",
- "t": [
- "mochikit","webdev","tool","tools",
- "javascript","interactive","interpreter","repl"
- ]
- }])
可見,由于使用圓括號括住了返回的數(shù)據(jù),這就相當于把一個JSONP請求轉(zhuǎn)化成了一次函數(shù)調(diào)用,或者得到了一個純粹的JSON直接量。服務(wù)器所要配合做的,就是在JSON數(shù)據(jù)的開頭添加一小段文本(即回調(diào)函數(shù)的名稱。——譯者注)并將JSON數(shù)據(jù)放在括號中!
當然,接下來最好是使用Mochikit、Dojo等框架來抽象JSONP,從而讓自己省去動手編寫DOM以插入script標簽的麻煩。
沒錯,JSONP只是解決了標準化的問題。假如遠程主機想通過script標簽向頁面中注入惡意代碼,而不是返回JSON數(shù)據(jù),那么頁面安全可能會 隨時受到威脅。不過,一旦實現(xiàn)了JSONP,那么對開發(fā)人員來說肯定是一件省時省力的大好事,在此基礎(chǔ)上各種一般化的抽象、教程及文檔也會應運而生的。
注:縮寫詞 JSONP 由 Bob Ippolito 在一篇名為 “Remote JSON - JSONP” 的文章中提出。但許多支持以 JSONP 技術(shù)實現(xiàn)跨域通信的廠商沒有稱其為 JSONP。例如,雅虎公司就稱這種技術(shù)為 “JSON with callbacks”。另外,原文發(fā)表于2005年12月5日。
留言(9)
大概是說通過script的src來實現(xiàn)跨域,通過經(jīng)編碼的json在url中傳遞數(shù)據(jù)?
是不是說頁面上的js生產(chǎn)帶有不同參數(shù)的jsonp,通過src傳遞給服務(wù)端,服務(wù)端根據(jù)該jsonp返回相應數(shù)據(jù)?有點不太明白,能否提供一個實例?謝謝
@vampire
你的理解是對的,看來我的翻譯你能看明白,呵呵。使用JSONP技術(shù)時,一般是由JS在客戶端頁面中動態(tài)插入script標簽,將其src屬性設(shè)置 為帶參數(shù)的URL。當頁面加載時,前述URL會將參數(shù)通過GET請求發(fā)送到相應服務(wù)器端應用程序(由URL表示),服務(wù)器根據(jù)參數(shù)返回數(shù)據(jù)——注意,這個 數(shù)據(jù)格式是JSON,并且(注意)被包含在一個函數(shù)調(diào)用中,例如:callback({json_data})。這樣,在客戶端頁面中存在預定義的 callback(data)函數(shù)的定義時,服務(wù)器返回的函數(shù)調(diào)用就會立即執(zhí)行,由客戶端的函數(shù)對數(shù)據(jù)進行操作。
實際的例子有很多,如Yahoo Search、Google Base、Flickr Search、Amazon Search等等。要學習和使用這些站點提供的支持JSONP的API,一般要注意兩點:一是有的站點要求注冊密鑰(使用時必須放到參數(shù)中),二是要注意 參數(shù)的使用方法。例如,有的API要求使用預定義的回調(diào)函數(shù),而有的API則允許使用者自己定義回調(diào)函數(shù)。
下面就是向Flickr Search請求JSONP響應的URL,其中使用了預定義的回調(diào)函數(shù)jsonFlickrApi(參數(shù)中不必給出):
http://api.flickr.com/services/rest/
?method=flickr.photos.search
&api_key=85618ad7d326d8ef93c6bee9ed32706f
&per_page=5&format=json&text=china
下面這個URL發(fā)送到Google Base,它允許開發(fā)人員使用自己定義的回調(diào)函數(shù):
http://www.google.com/base/feeds/snippets
?q=jquery
&alt=json-in-script
&callback=customCallback
把前面的URL放到瀏覽器地址欄中,回車,即可看到結(jié)果。
中秋好。
怎么把我的鏈接去掉了呢?
給我做個鏈接可以嗎???
非常感謝!!
我的EMAIL:hardcometure@163.com