說說JSON和JSONP 也許你會豁然開朗

來源:互聯網
上載者:User

前言

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

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

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

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

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

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

  什麼是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屬性方法來調用,是不是很爽呀?

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.