天天看點

ajax 和 jsonp 詳解

由于Sencha Touch 2這種開發模式的特性,基本決定了它原生的資料互動行為幾乎隻能通過AJAX來實作。

當然了,通過調用強大的PhoneGap插件然後打包,你可以實作100%的Socket通訊和本地資料庫功能,又或者通過HTML5的WebSocket也可以實作與伺服器的通訊和服務端推功能,但這兩種方式都有其局限性,前者需要PhoneGap支援,後者要求使用者裝置必須支援WebSocket,是以都不能算是ST2的原生解決方案,原生的隻有AJAX。

說到AJAX就會不可避免的面臨兩個問題,第一個是AJAX以何種格式來交換資料?第二個是跨域的需求如何解決?這兩個問題目前都有不同的解決方案,比如資料可以用自定義字元串或者用XML來描述,跨域可以通過伺服器端代理來解決。

但到目前為止最被推崇或者說首選的方案還是用JSON來傳資料,靠JSONP來跨域。而這就是本文将要講述的内容。

JSON和JSONP雖然隻有一個字母的差别,但其實他們根本不是一回事兒:JSON是一種資料交換格式,而JSONP是一種依靠開發人員的聰明才智創造出的一種非官方跨域資料互動協定。我們拿最近比較火的諜戰片來打個比方,JSON是地下黨們用來書寫和交換情報的“暗号”,而JSONP則是把用暗号書寫的情報傳遞給自己同志時使用的接頭方式。看到沒?一個是描述資訊的格式,一個是資訊傳遞雙方約定的方法。

既然随便聊聊,那我們就不再采用教條的方式來講述,而是把關注重心放在幫助開發人員了解是否應當選擇使用以及如何使用上。

小小的廣告一下,該篇文章是在自己群裡與Sencha Touch 2的開發者們一起探讨ST2資料互動模型時有感而發寫出來的,是以如果您對Mobile Web App開發有興趣的話,歡迎加入Sencha Touch 交流 QQ 群 213119459 。

 什麼是JSON?

前面簡單說了一下,JSON是一種基于文本的資料交換方式,或者叫做資料描述格式,你是否該選用他首先肯定要關注它所擁有的優點。

JSON的優點:

1、基于純文字,跨平台傳遞極其簡單;

2、Javascript原生支援,背景語言幾乎全部支援;

3、輕量級資料格式,占用字元數量極少,特别适合網際網路傳遞;

4、可讀性較強,雖然比不上XML那麼一目了然,但在合理的依次縮進之後還是很容易識别的;

5、容易編寫和解析,當然前提是你要知道資料結構;

JSON的缺點當然也有,但在作者看來實在是無關緊要的東西,是以不再單獨說明。

JSON的格式或者叫規則:

JSON能夠以非常簡單的方式來描述資料結構,XML能做的它都能做,是以在跨平台方面兩者完全不分伯仲。

1、JSON隻有兩種資料類型描述符,大括号{}和方括号[],其餘英文冒号:是映射符,英文逗号,是分隔符,英文雙引号""是定義符。

2、大括号{}用來描述一組“不同類型的無序鍵值對集合”(每個鍵值對可以了解為OOP的屬性描述),方括号[]用來描述一組“相同類型的有序資料集合”(可對應OOP的數組)。

3、上述兩種集合中若有多個子項,則通過英文逗号,進行分隔。

4、鍵值對以英文冒号:進行分隔,并且建議鍵名都加上英文雙引号"",以便于不同語言的解析。

5、JSON内部常用資料類型無非就是字元串、數字、布爾、日期、null 這麼幾個,字元串必須用雙引号引起來,其餘的都不用,日期類型比較特殊,這裡就不展開講述了,隻是建議如果用戶端沒有按日期排序功能需求的話,那麼把日期時間直接作為字元串傳遞就好,可以省去很多麻煩。

JSON執行個體:

複制代碼

// 描述一個人

var person = {

    "Name": "Bob",

    "Age": 32,

    "Company": "IBM",

    "Engineer": true

}

// 擷取這個人的資訊

var personAge = person.Age;

// 描述幾個人

var members = [

    {

        "Name": "Bob",

        "Age": 32,

        "Company": "IBM",

        "Engineer": true

    },

    {

        "Name": "John",

        "Age": 20,

        "Company": "Oracle",

        "Engineer": false

    },

    {

        "Name": "Henry",

        "Age": 45,

        "Company": "Microsoft",

        "Engineer": false

    }

]

// 讀取其中John的公司名稱

var johnsCompany = members[1].Company;

// 描述一次會議

var conference = {

    "Conference": "Future Marketing",

    "Date": "2012-6-1",

    "Address": "Beijing",

    "Members": 

    [

        {

            "Name": "Bob",

            "Age": 32,

            "Company": "IBM",

            "Engineer": true

        },

        {

            "Name": "John",

            "Age": 20,

            "Company": "Oracle",

            "Engineer": false

        },

        {

            "Name": "Henry",

            "Age": 45,

            "Company": "Microsoft",

            "Engineer": false

        }

    ]

}

// 讀取參會者Henry是否工程師

var henryIsAnEngineer = conference.Members[2].Engineer;

複制代碼

關于JSON,就說這麼多,更多細節請在開發過程中查閱資料深入學習。

 什麼是JSONP?

先說說JSONP是怎麼産生的:

其實網上關于JSONP的講解有很多,但卻千篇一律,而且雲裡霧裡,對于很多剛接觸的人來講了解起來有些困難,小可不才,試着用自己的方式來闡釋一下這個問題,看看是否有幫助。

1、一個衆所周知的問題,Ajax直接請求普通檔案存在跨域無權限通路的問題,甭管你是靜态頁面、動态網頁、web服務、WCF,隻要是跨域請求,一律不準;

2、不過我們又發現,Web頁面上調用js檔案時則不受是否跨域的影響(不僅如此,我們還發現凡是擁有"src"這個屬性的标簽都擁有跨域的能力,比如<script>、<img>、<iframe>);

3、于是可以判斷,目前階段如果想通過純web端(ActiveX控件、服務端代理、屬于未來的HTML5之Websocket等方式不算)跨域通路資料就隻有一種可能,那就是在遠端伺服器上設法把資料裝進js格式的檔案裡,供用戶端調用和進一步處理;

4、恰巧我們已經知道有一種叫做JSON的純字元資料格式可以簡潔的描述複雜資料,更妙的是JSON還被js原生支援,是以在用戶端幾乎可以随心所欲的處理這種格式的資料;

5、這樣子解決方案就呼之欲出了,web用戶端通過與調用腳本一模一樣的方式,來調用跨域伺服器上動态生成的js格式檔案(一般以JSON為字尾),顯而易見,伺服器之是以要動态生成JSON檔案,目的就在于把用戶端需要的資料裝入進去。

6、用戶端在對JSON檔案調用成功之後,也就獲得了自己所需的資料,剩下的就是按照自己需求進行處理和展現了,這種擷取遠端資料的方式看起來非常像AJAX,但其實并不一樣。

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

如果對于callback參數如何使用還有些模糊的話,我們後面會有具體的執行個體來講解。

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頁面代碼如下:

複制代碼

<!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">

    var localHandler = function(data){

        alert('我是本地函數,可以被跨域的remote.js檔案調用,遠端js帶來的資料是:' + data.result);

    };

    </script>

    <script type="text/javascript" src="http://remoteserver.com/remote.js"></script>

</head>

<body>

</body>

</html>

複制代碼

remote.js檔案代碼如下:

localHandler({"result":"我是遠端js帶來的資料"});

運作之後檢視結果,頁面成功彈出提示視窗,顯示本地函數被跨域的遠端js調用成功,并且還接收到了遠端js帶來的資料。很欣喜,跨域遠端擷取資料的目的基本實作了,但是又一個問題出現了,我怎麼讓遠端js知道它應該調用的本地函數叫什麼名字呢?畢竟是jsonp的服務者都要面對很多服務對象,而這些服務對象各自的本地函數都不相同啊?我們接着往下看。

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

看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">

    // 得到航班資訊查詢結果後的回調函數

    var flightHandler = function(data){

        alert('你查詢的航班結果是:票價 ' + data.price + ' 元,' + '餘票 ' + data.tickets + ' 張。');

    };

    // 提供jsonp服務的url位址(不管是什麼類型的位址,最終生成的傳回值都是一段javascript代碼)

    var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";

    // 建立script标簽,設定其屬性

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

    script.setAttribute('src', url);

    // 把script标簽加入head,此時調用開始

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

    </script>

</head>

<body>

</body>

</html>

複制代碼

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

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

OK,伺服器很聰明,這個叫做flightResult.aspx的頁面生成了一段這樣的代碼提供給jsonp.html(服務端的實作這裡就不示範了,與你選用的語言無關,說到底就是拼接字元串):

flightHandler({

    "code": "CA1998",

    "price": 1780,

    "tickets": 5

});

我們看到,傳遞給flightHandler函數的是一個json,它描述了航班的基本資訊。運作一下頁面,成功彈出提示視窗,jsonp的執行全過程順利完成!

4、到這裡為止的話,相信你已經能夠了解jsonp的用戶端實作原理了吧?剩下的就是如何把代碼封裝一下,以便于與使用者界面互動,進而實作多次和重複調用。

什麼?你用的是jQuery,想知道jQuery如何實作jsonp調用?好吧,那我就好人做到底,再給你一段jQuery使用jsonp的代碼(我們依然沿用上面那個航班資訊查詢的例子,假定傳回jsonp結果不變):

複制代碼

<!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>Untitled Page</title>

      <script type="text/javascript" src=jquery.min.js"></script>

      <script type="text/javascript">

     jQuery(document).ready(function(){ 

        $.ajax({

             type: "get",

             async: false,

             url: "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998",

             dataType: "jsonp",

             jsonp: "callback",//傳遞給請求處理程式或頁面的,用以獲得jsonp回調函數名的參數名(一般預設為:callback)

             jsonpCallback:"flightHandler",//自定義的jsonp回調函數名稱,預設為jQuery自動生成的随機函數名,也可以寫"?",jQuery會自動為你處理資料

             success: function(json){

                 alert('您查詢到航班資訊:票價: ' + json.price + ' 元,餘票: ' + json.tickets + ' 張。');

             },

             error: function(){

                 alert('fail');

             }

         });

     });

     </script>

     </head>

  <body>

  </body>

 </html>

複制代碼

是不是有點奇怪?為什麼我這次沒有寫flightHandler這個函數呢?而且竟然也運作成功了!哈哈,這就是jQuery的功勞了,jquery在處理jsonp類型的ajax時(還是忍不住吐槽,雖然jquery也把jsonp歸入了ajax,但其實它們真的不是一回事兒),自動幫你生成回調函數并把資料取出來供success屬性方法來調用,是不是很爽呀?

好啦,寫到這裡,我已經無力再寫下去,又困又累,得趕緊睡覺。朋友們要是看這不錯,覺得有啟發,給點個“推薦”呗!由于實在比較簡單,是以就不再提供demo源碼下載下傳了。

 4月20日下午的補充:

沒想到上了部落格園的頭條推薦。看到大家對這篇文章的認可和評論,還是很開心的,這裡針對ajax與jsonp的異同再做一些補充說明:

1、ajax和jsonp這兩種技術在調用方式上“看起來”很像,目的也一樣,都是請求一個url,然後把伺服器傳回的資料進行處理,是以jquery和ext等架構都把jsonp作為ajax的一種形式進行了封裝;

2、但ajax和jsonp其實本質上是不同的東西。ajax的核心是通過XmlHttpRequest擷取非本頁内容,而jsonp的核心則是動态添加<script>标簽來調用伺服器提供的js腳本。

3、是以說,其實ajax與jsonp的差別不在于是否跨域,ajax通過服務端代理一樣可以實作跨域,jsonp本身也不排斥同域的資料的擷取。

4、還有就是,jsonp是一種方式或者說非強制性協定,如同ajax一樣,它也不一定非要用json格式來傳遞資料,如果你願意,字元串都行,隻不過這樣不利于用jsonp提供公開服務。

總而言之,jsonp不是ajax的一個特例,哪怕jquery等巨頭把jsonp封裝進了ajax,也不能改變着一點!

繼續閱讀