JSONP跨域原理和jQuery.getJSON用法
JSONP是一個非官方的協(xié)議,它允許在服務(wù)器端集成Script tags返回至客戶端,通過javascript callback的形式實現(xiàn)跨域訪問(這僅僅是JSONP簡單的實現(xiàn)形式)。JSON系統(tǒng)開發(fā)方法是一種典型的面向數(shù)據(jù)結(jié)構(gòu)的分析和設(shè)計方法,以活動為中心,一連串的活動的順序組合成一個完整的工作進程。
之所以會有跨域這個問題的產(chǎn)生根本原因是瀏覽器的同源策略限制,理解同源策略的限制同源策略是指阻止代碼獲得或者更改從另一個域名下獲得的文件或者信息。也就是說我們的請求地址必須和當(dāng)前網(wǎng)站的地指相同。同源策略通過隔離來實現(xiàn)對資源的保護。這個策略的歷史非常悠久從Netscape Navigator 2.0時代就開始了。
解決這個限制的一個相對簡單的辦法就是在服務(wù)器端發(fā)送請求,服務(wù)器充當(dāng)一個到達第三方資源的代理中繼。雖然是用廣泛但是這個方法卻不夠靈活。
另一個辦法就是使用框架(frames),將第三方站點的資源包含進來,但是包含進來的資源同樣要受到同源策略的限制。
有一個很巧妙的辦法就是在頁面中使用動態(tài)代碼元素,代碼的源指向服務(wù)地址并在自己的代碼中加載數(shù)據(jù)。當(dāng)這些代碼加載執(zhí)行的時候,同源策略就不會起到限制。但是如果代碼試圖下載文件的時候執(zhí)行還是會失敗,幸運的是,我們可以使用JSON(JavaScript Object Notation)來改進這個應(yīng)用。
JSON和JSONP
與XML相比,JSON是一個輕量級的數(shù)據(jù)交換格式。JSON對于JavaScript開發(fā)人員充滿魅力的原因在于JSON本身就是Javascript中的對象。
例如一個ticker對象
var ticker = {symbol:'IBM',price:100}
而JSON串就是 {symbol:'IBM',price:100}
這樣我們就可以在函數(shù)的參數(shù)中傳遞JSON數(shù)據(jù)。我們很容易掌握在函數(shù)中使用動態(tài)的JSON參數(shù)數(shù)據(jù),但是我們的目的并不是這個。
通過使我們的函數(shù)能夠加載動態(tài)的JSON數(shù)據(jù),我們就能夠處理動態(tài)的數(shù)據(jù),這項技術(shù)叫做 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);
上面的代碼通過動態(tài)加入Javascript代碼,來執(zhí)行函數(shù)加載數(shù)據(jù)。
正如之前提到過的,同源策略對于動態(tài)插入的代碼不適用。也就是你可以從不同的域中加載代碼,來執(zhí)行在他們代碼中的JSON數(shù)據(jù)。
這就是JSONP(JSON with Padding)。注意,使用這種方法時,你必須在頁面中定義回調(diào)函數(shù),就像上例中的showPrice一樣。
我們通常所說的JSONP服務(wù)(遠程JSON服務(wù)),實際上就是一種擴展的支持在用戶定義函數(shù)中包含返回數(shù)據(jù)的能力。這種方法依賴于必須接受一個回調(diào)函數(shù)的名字作為參數(shù)。
然后執(zhí)行這個函數(shù),處理JSON數(shù)據(jù),并顯示在客戶頁面上。
JQuery的JSONP支持
從JQery 1.2以后,就開始支持JSONP的調(diào)用。在另外的一個域名中指定好回調(diào)函數(shù)名稱,你就可以用下面的形式來就加載JSON數(shù)據(jù)。
url?callback=?
示例:
- jQuery.getJSON(url + "&callbak=?", function(data)
- {
- alert("Symbol:" + data.symbol + ", Price:" + data.price);
- });
jquery會在window對象中加載一個全局的函數(shù),當(dāng)代碼插入時函數(shù)執(zhí)行,執(zhí)行完畢后就會被移除。同時jquery還對非跨域的請求進行了優(yōu)化,如果這個請求是在同一個域名下那么他就會像正常的Ajax請求一樣工作。
上例中我們在動態(tài)插入到頁面的代碼中使用了靜態(tài)的json數(shù)據(jù),雖然完成了依次JSONP返回,但仍不是JSONP服務(wù),因為不支持在URL中定義回調(diào)函數(shù)名稱。下面是一個將其變成JSONP服務(wù)的一個方法
服務(wù)器端使用PHP。
首先我們來定義接口的規(guī)范,就像這樣:http://www.mydomain.com/jsonp/ticker?symbol=IBM&callback=showPrice
symbol是請求條件,callback是回調(diào)函數(shù)名稱。
在頁面文件中,我們使用JQuery的支持:
- //JQuery JSONP Support
- var url = "http://www.mydomain.com/api/suggest.php?symbol=IBM&callback=?";
- jQuery.getJSON(url, function(data){
- alert("Symbol:" + data.symbol + ", Price:" + data.price);
- });
在suggest.php中
- $jsondata = "{symbol:'IBM', price:120}";
- echo $_GET['callback'].'('.$jsondata.')';
再舉個.NET webservice 的例子
客戶端
- $.getJSON(
- "http://192.168.0.66/services/WebService1.asmx/ws?callback=?",
- { name: "ff", time: "2pm" },
- function(data) { alert(decodeURI(data.msg)) }
- );
服務(wù)器端
- [WebMethod]
- public void ws(string name,string time) {
- HttpRequest Request = HttpContext.Current.Request;
- string callback = Request["callback"];
- HttpResponse Response = HttpContext.Current.Response;
- Response.Write(callback + "({msg:'this is"+name+"jsonp'})");
- Response.End();
- }
現(xiàn)在,如果我們想制作一些mashup,或者將第三方的資源整合到一個頁面中,我們就很容易想到JSONP的解決方法了。
注意:
JSONP是一個非常強大的構(gòu)建mashp的方法,可是不是一個解決跨域訪問問題的***藥。它也有一些缺點:
***也是最重要的:JSONP不提供錯誤處理。如果動態(tài)插入的代碼正常運行,你可以得到返回,但是如果失敗了,那么什么都不會發(fā)生。你無法獲得一個404的錯誤,也不能取消這個請求。
另外一個重要的缺點是如果使用了不信任的服務(wù)會造成很大的安全隱患。
【編輯推薦】