天天看點

使用Jsonp解決跨域資料通路問題

簡介

符合Web2.0特征的衆多網站一個明顯的特點就是采用Ajax。Ajax提供了在背景送出請求通路資料的功能。其實作主要使用的是XMLHttpRequest函數,這個函數允許用戶端的Javascript

發送到伺服器端的HTTP請求并獲得傳回資料。Ajax同時也是目前衆多的Mashup背後的驅動力量,他們都利用Ajax來聚合不同來源的資訊。

了解同源政策的限制

同源政策是指阻止代碼獲得或者更改從另一個域名下獲得的檔案或者資訊。也就是說我們的請求位址必須和目前網站的地指相同。同源政策通過隔離來實作對資源的保護。這個政策的曆史非常悠久

從Netscape Navigator 2.0時代就開始了。

解決這個限制的一個相對簡單的辦法就是在伺服器端發送請求,伺服器充當一個到達第三方資源的代理中繼。雖然是用廣泛但是這個方法卻不夠靈活。

另一個辦法就是使用架構(frames),将第三方站點的資源包含進來,但是包含進來的資源同樣要受到同源政策的限制。

有一個很巧妙的辦法就是在頁面中使用動态代碼元素,代碼的源指向服務位址并在自己的代碼中加載資料。當這些代碼加載執行的時候,同源政策就不會起到限制。但是如果代碼試圖下載下傳檔案的時候

執行還是會失敗,幸運的是,我們可以使用JSON(JavaScript Object Notation)來改進這個應用。

JSON和JSONP

與XML相比,JSON是一個輕量級的資料交換格式。JSON對于JavaScript開發人員充滿魅力的原因在于JSON本身就是Javascript中的對象。

例如一個ticker對象

    var ticker = {symbol:'IBM',price:100}

而JSON串就是    {symbol:'IBM',price:100}

這樣我們就可以在函數的參數中傳遞JSON資料。我們很容易掌握在函數中使用動态的JSON參數資料,但是我們的目的并不是這個。

通過使我們的函數能夠加載動态的JSON資料,我們就能夠處理動态的資料,這項技術叫做 Dynamic Javascript Insertion。

我們看下面的例子

index.html中

    <script type="text/javascript">

        function showPrice(data){

            alert("Symbol:" + data.symbol + ", Price:" + data.price);

        }

        var url = "ticker.js";        //Outer JS URL

        var script = document.createElement('script');

        script.setAttribute('src', url);

        //load javascript

        document.getElementsByTagName('head')[0].appendChild(script);

    </script>

ticker.js中

    var data = {symbol:'IBM', price:100};

    showPrice(data);

上面的代碼通過動态加入Javascript代碼,來執行函數加載資料。

正如之前提到過的,同源政策對于動态插入的代碼不适用。也就是你可以從不同的域中加載代碼,來執行在他們代碼中的JSON資料。

這就是JSONP(JSON with Padding)。注意,使用這種方法時,你必須在頁面中定義回調函數,就像上例中的showPrice一樣。

我們通常所說的JSONP服務(遠端JSON服務),實際上就是一種擴充的支援在使用者定義函數中包含傳回資料的能力。這種方法依賴于必須接受一個回調函數的名字作為參數。

然後執行這個函數,處理JSON資料,并顯示在客戶頁面上。

JQuery的JSONP支援

從JQery 1.2以後,就開始支援JSONP的調用。在另外的一個域名中指定好回調函數名稱,你就可以用下面的形式來就加載JSON資料。

    url?callback=?

示例:

    jQuery.getJSON(url + "&callbak=?", function(data){

        alert("Symbol:" + data.symbol + ", Price:" + data.price);

    });

jquery會在window對象中加載一個全局的函數,當代碼插入時函數執行,執行完畢後就會被移除。同時jquery還對非跨域的請求進行了優化,如果這個請求是在同一個域名下

那麼他就會像正常的Ajax請求一樣工作。

上例中我們在動态插入到頁面的代碼中使用了靜态的json資料,雖然完成了依次JSONP傳回,但仍不是JSONP服務,因為不支援在URL中定義回調函數名稱。下面是一個将其變成JSONP服務的一個方法

伺服器端使用PHP。

首先我們來定義接口的規範,就像這樣:http://www.mydomain.com/jsonp/ticker?symbol=IBM&callback=showPrice

symbol是請求條件,callback是回調函數名稱。

在頁面檔案中,我們使用JQuery的支援:

        //JQuery JSONP Support

        var url = "http://www.mydomain.com/api/suggest.php?symbol=IBM&callback=?";

        jQuery.getJSON(url, function(data){

        });

在suggest.php中

     $jsondata = "{symbol:'IBM', price:120}";

     echo $_GET['callback'].'('.$jsondata.')';

現在,如果我們想制作一些mashup,或者将第三方的資源整合到一個頁面中,我們就很容易想到JSONP的解決方法了。

現有的JSONP服務

    既然我們已經知道如何使用JSONP,那麼我們也就可以使用一些現有的JSONP服務了,下面是一些例子:

    Digg API:http://services.digg.com/stories/top?appkey=http%3A%2F%2Fmashup.com&type=javascript&callback=?

    Geonames API:http://www.geonames.org/postalCodeLookupJSON?postalcode=10504&country=US&callback=?

    Flickr API:http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?

注意:

    JSONP是一個非常強大的建構mashp的方法,可是不是一個解決跨域通路問題的萬能藥。它也有一些缺點

    第一也是最重要的:JSONP不提供錯誤處理。如果動态插入的代碼正常運作,你可以得到傳回,但是如果失敗了,那麼什麼都不會發生。你無法獲得一個404的錯誤,也不能取消這個請求

    另外一個重要的缺點是如果使用了不信任的服務會造成很大的安全隐患。    

[參考資料]

1、Cross-domain communication with jsonp        http://www.ibm.com/developerworks/library/wa-aj-jsonp1/