JavaScript 跨域調用 JSONP

openkk 13年前發布 | 32K 次閱讀 JSONP JSON開發包

瀏覽器安全模型規定,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:

    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似乎沒有什么,但我們可以要求服務器在數據有效時給出通知。因此,我可以編寫一個用于跟蹤數據的小系統:

    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");

根據以上假設,用于獲取數據的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"
    ]
    }])

可見,由于使用圓括號括住了返回的數據,這就相當于把一個JSONP請求轉化成了一次函數調用,或者得到了一個純粹的JSON直接量。服務器所要配合做的,就是在JSON數據的開頭添加一小段文本(即回調函數的名稱。——譯者注)并將JSON數據放在括號中!

當然,接下來最好是使用Mochikit、Dojo等框架來抽象JSONP,從而讓自己省去動手編寫DOM以插入script標簽的麻煩。

沒錯,JSONP只是解決了標準化的問題。假如遠程主機想通過script標簽向頁面中注入惡意代碼,而不是返回JSON數據,那么頁面安全可能會 隨時受到威脅。不過,一旦實現了JSONP,那么對開發人員來說肯定是一件省時省力的大好事,在此基礎上各種一般化的抽象、教程及文檔也會應運而生的。

項目主頁:http://www.baiduhome.net/lib/view/home/1324129562640

 本文由用戶 openkk 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!