1. 程式人生 > >jsonp原理詳解

jsonp原理詳解

col 又一 開發 針對 img fig -c 本地服務器 其他

先說說JSONP是怎麽產生的:

1、一個眾所周知的問題,ajax直接請求普通文件存在跨域無權限訪問的問題,甭管你是靜態頁面、動態頁面、web服務、WCF,只要是跨域請求,一律不準。

2、不過我們又發現,web頁面上調用js文件時則不受跨域的影響(不僅如此,我們還發現凡是擁有“src”這個屬性的標簽都擁有跨域的能力,比如<script>、<img>、<iframe>)。

3、於是可以判斷,當前階段如果想通過純web端(ActiveX控件、服務端代理、Websocket等方式不算)跨域訪問數據就只有一種可能,那就是在遠程服務器上設法把數據裝進js格式的文件裏,供客戶端調用和進一步處理。

4、恰巧我們已經知道有一種叫做JSON的純字符數據格式可以簡潔的描述復雜數據,更妙的是JSON還被js原生支持,所以在客戶端幾乎可以隨心所欲的處理這種格式的數據。

5、這樣子解決方案就呼之欲出了,web客戶端通過與調用腳本一模一樣的方式,來調用跨域服務器上動態生成的js格式文件(一般以JSON為後綴),顯而易見,服務器之所以要動態生成JSON文件,目的就在於把客戶端需要的數據裝進去。

6、客戶端在對JSON文件調用成功之後,也就獲得了自己所需的數據,剩下的就是按照自己需求進行處理和展現了,這種獲取遠程數據的方式看起來非常想ajax,但其實並不一樣。

7、為了便於客戶端使用數據,逐漸形成了一種非正式的傳輸協議,人們把它稱作jsonp,該協議的一個要點就是允許用戶傳遞一個callback參數給服務端,然後服務端返回數據時會將這個callback參數作為函數名來包裹住json數據,這樣客戶端就可以隨意定制自己的函數來自動處理返回數據了。

jsonp的客戶端具體實現:

  不管jquery也好,extjs也罷,又或者是其他支出jsonp的框架,他們幕後所做的工作都是一樣的,廈門來循序漸進的說明一下jsonp在客戶端的實現:

1、我們知道,哪怕是跨域js文件中的代碼(當然指符合web腳本安全策略的),web頁面也是可以無條件執行的。

  遠程服務器 remoteserver.com 根目錄下有個 remote.js 文件,代碼如下:

alert(‘我是遠程文件‘);

本地服務器 localserver.com 下有個jsonp.html 頁面代碼如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript" src="http://remoteserver.com/remote.js"></script> </head> <body> </body> </html>

毫無疑問,頁面將會彈出一個提示窗體,顯示跨域調用成功。

2、現在我們在jsonp.html 頁面定義一個函數,然後在遠程remote.js 中傳入數據進行調用。

jsonp.html 頁面代碼如下:

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml">
 3 <head>
 4     <title></title>
 5     <script type="text/javascript">
 6     var localHandler = function(data){
 7         alert(我是本地函數,可以被跨域的remote.js文件調用,遠程js帶來的數據是: + data.result);
 8     };
 9     </script>
10     <script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
11 </head>
12 <body>
13  
14 </body>
15 </html>

remote.js 文件代碼如下:

localHandler({"result":"我是遠程js帶來的數據"});

  運行之後查看結果,頁面成功彈出提示窗口,顯示本地函數被跨域的遠程js調用成功,並且還接收到了遠程js帶來的數據。

  很欣喜,跨域遠程獲取數據的目的基本實現了,但是又一個問題出現了,我怎麽讓遠程js知道它應該調用的本地函數叫什麽名字呢?畢竟是jsonp的服務者都要面對很多服務對象,而這些服務對象各自的本地函數都不相同啊?我們接著往下看。

3、聰明的開發者很容易想到,只要服務端提供的js腳本是動態生成的就行了唄,這樣調用者可以傳一個參數過去告訴服務端“我想要一段調用XXX函數的js代碼,請你返回給我”,預算服務器就可以按照客戶端的需求來生成js腳本並響應了。

看jsonp.html 頁面的代碼:

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml">
 3 <head>
 4     <title></title>
 5     <script type="text/javascript">
 6     // 得到航班信息查詢結果後的回調函數
 7     var flightHandler = function(data){
 8         alert(你查詢的航班結果是:票價  + data.price +  元, + 余票  + data.tickets +  張。);
 9     };
10     // 提供jsonp服務的url地址(不管是什麽類型的地址,最終生成的返回值都是一段javascript代碼)
11     var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";
12     // 創建script標簽,設置其屬性
13     var script = document.createElement(script);
14     script.setAttribute(src, url);
15     // 把script標簽加入head,此時調用開始
16     document.getElementsByTagName(head)[0].appendChild(script); 
17     </script>
18 </head>
19 <body>
20  
21 </body>
22 </html>

  這次的代碼變化比較大,不再直接把遠程js文件寫死,而是編碼實現動態查詢,而這也正是jsonp客戶端實現的核心部分,本例中的重點也就在於如何完成jsonp調用的全過程。

  我們看到調用的url中傳遞了一個code參數,告訴服務器我要查的是CA1998次航班的信息,而callback參數則告訴服務器,我的本地回調函數叫做fightHandler,所以請把查詢結果傳入中國函數中進行調用。

  OK,服務器很聰明,中國叫做fightResult.aspx 的頁面生成了一段這樣的代碼提供給jsonp.html(服務端的實現這裏就不演示了,與你選用的語言無關,說到底就是拼接字符串)

flightHandler({
    "code": "CA1998",
    "price": 1780,
    "tickets": 5
});

4、到這裏為止,剩下的就是如何把代碼封裝一下,以便於與用戶界面交互,從而實現多次和重復調用。

jquery如何實現jsonp調用?

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml" >
 3 <head>
 4      <title>Untitled Page</title>
 5       <script type="text/javascript" src=jquery.min.js"></script>
 6       <script type="text/javascript">
 7      jQuery(document).ready(function(){ 
 8         $.ajax({
 9              type: "get",
10              async: false,
11              url: "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998",
12              dataType: "jsonp",
13              jsonp: "callback",//傳遞給請求處理程序或頁面的,用以獲得jsonp回調函數名的參數名(一般默認為:callback)
14              jsonpCallback:"flightHandler",//自定義的jsonp回調函數名稱,默認為jQuery自動生成的隨機函數名,也可以寫"?",jQuery會自動為你處理數據
15              success: function(json){
16                  alert(您查詢到航班信息:票價:  + json.price +  元,余票:  + json.tickets +  張。);
17              },
18              error: function(){
19                  alert(fail);
20              }
21          });
22      });
23      </script>
24      </head>
25   <body>
26   </body>
27 </html>

是不是有點奇怪?為什麽我這次沒有血flightHandler 這個函數呢?而且竟然也運行成功了!

這就是jquery的功勞了,jquery在處理jsonp類型的ajax時(雖然jquery也把jsonp歸入了ajax,但其實它們真的不是一回事兒),自動幫你生成回調函數並把數據取出來供success屬性方法來調用,是不是很爽啊?

補充:

這裏針對ajax與jsonp的異同再做一些補充說明:

1、ajax和jsonp這兩種技術在調用方式上“看起來”很像,目的也一樣,都是請求一個url,然後把服務器返回的數據進行處理,因此jquery和ext等框架都把jsonp作為ajax的一種形式進行了封裝。

2、但ajax和jsonp其實本質上是不同的東西。ajax的核心是通過XMLHttpRequest獲取非本頁內容,而jsonp的核心是動態添加。

jsonp原理詳解