JSON與JSONP

JSON和JSONP的區別

簡要來說JSON是數據交換格式,JSONP是實現跨域的一種方法

什么是JSON

JSON是一種基于文本的數據交換方式,或者叫做數據描述格式。

JSON的格式或者叫規則

JSON能夠以非常簡單的方式來描述數據結構,XML能做的它都能做,因此在跨平臺方面兩者完全不分伯仲。

  • JSON只有兩種數據類型描述符,大括號{}和方括號[],其余英文冒號:是映射符,英文逗號,是分隔符,英文雙引號""是定義符。
  • 大括號{}用來描述一組“不同類型的無序鍵值對集合”(每個鍵值對可以理解為OOP的屬性描述),方括號[]用來描述一組“相同類型的有序數據集合”(可對應OOP的數組)。
  • 上述兩種集合中若有多個子項,則通過英文逗號,進行分隔。
  • 鍵值對以英文冒號:進行分隔,并且建議鍵名都加上英文雙引號"",以便于不同語言的解析。
  • JSON內部常用數據類型無非就是字符串、數字、布爾、日期、null 這么幾個,字符串必須用雙引號引起來,其余的都不用,日期類型比較特殊,這里就不展開講述了,只是建議如果客戶端沒有按日期排序功能需求的話,那么把日期時間直接作為字符串傳遞就好,可以省去很多麻煩。
// 描述一個人

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格式是否符合規范 bejson

什么是JSONP?

先說說為什么要用JSONP

  • 一個眾所周知的問題,Ajax直接請求普通文件存在跨域無權限訪問的問題;
  • 不過我們又發現,Web頁面上調用js文件時則不受是否跨域的影響(不僅如此,我們還發現凡是擁有"src"這個屬性的標簽都擁有跨域的能力,比如scriptimgiframe);
  • 這樣子解決方案就呼之欲出了,web客戶端通過與調用腳本一模一樣的方式,來調用跨域服務器上動態生成的js格式文件(一般以JSON為后綴),顯而易見,服務器之所以要動態生成JSON文件,目的就在于把客戶端需要的數據裝入進去。
  • 為了便于客戶端使用數據,逐漸形成了一種非正式傳輸協議,人們把它稱作JSONP,該協議的一個要點就是允許用戶傳遞一個callback參數給服務端,然后服務端返回數據時會將這個callback參數作為函數名來包裹住JSON數據,這樣客戶端就可以隨意定制自己的函數來自動處理返回數據了。
    如果對于callback參數如何使用還有些模糊的話,我們后面會有具體的實例來講解。

1、我們知道,哪怕跨域js文件中的代碼(當然指符合web腳本安全策略的),web頁面也是可以無條件執行的。
在命令行下執行 node server1.js, server1.js內容為:

var http = require('http');  
http.createServer(function (req, res) {  
  res.writeHead(200, {'Content-Type': 'application/json'});  
    res.end('alert("我是遠程文件")');
}).listen(8080);  
console.log('Server running on port 8080.');

在瀏覽器中訪問"http://localhost:8080"
jsonp1.html script src指向http://localhost:8080,具體代碼如下

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8" />
  <title></title>
  <script src="http://localhost:8080"></script>
 </head>
 <body>
 </body>
</html>

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

2、現在我們在jsonp2.html頁面定義一個函數,然后在server2.js中傳入數據進行調用。
jsonp2.html內容

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8" />
  <title></title>
  <script>
   var localHandler = function(data) {
    alert('我是本地函數,可以被跨域的remote.js文件調用,遠程js帶來的數據是:' + data.result);
   };
  </script>
  <script src="http://localhost:8080"></script>
 </head>
 <body>
 </body>
</html>

server2.js 內容

var http = require('http');  
http.createServer(function (req, res) {  
  res.writeHead(200, {'Content-Type': 'application/json'});  
   res.end('localHandler({"result":"我是遠程js帶來的數據"})');
}).listen(8080);  

console.log('Server running on port 8080.');

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

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

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8" />
  <title></title>
  <script type="text/javascript">
   // 得到航班信息查詢結果后的回調函數
   var flightHandler = function(data) {
    alert('你查詢的航班結果是:票價 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 張。');
   };
   // 提供jsonp服務的url地址(不管是什么類型的地址,最終生成的返回值都是一段javascript代碼)
   var url = "http://localhost:8080?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中傳遞了一個callback參數則告訴服務器,我的本地回調函數叫做flightHandler,所以請把查詢結果傳入這個函數中進行調用。
OK,服務器很聰明,這個叫做server3.js的頁面生成了一段這樣的代碼提供給jsonp3.html。server3.js的頁面代碼:

var http = require('http');  
var URL = require('url');

http.createServer(function (req, res) {  
  res.writeHead(200, {'Content-Type': 'application/json'});  
  var params = URL.parse(req.url, true).query;
   res.end(params.callback+'({"code": "CA1998","price": 1780,"tickets": 5})');
 }).listen(8080);  

console.log('Server running on port 8080.');

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

4、到這里為止的話,相信你已經能夠理解jsonp的客戶端實現原理了吧?剩下的就是如何把代碼封裝一下,以便于與用戶界面交互,從而實現多次和重復調用。
什么?你用的是jQuery,想知道jQuery如何實現jsonp調用?好吧,那我就好人做到底,再給你一段jQuery使用jsonp的代碼(我們依然沿用上面那個航班信息查詢的例子,假定返回jsonp結果不變):

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8" />
  <title></title>
  <script src="http://apps.bdimg.com/libs/jquery/1.6.4/jquery.js"></script>
  <script type="text/javascript">
   $.ajax({
    type: "get",
    async: false,
    url: "http://localhost:8080",
    dataType: "jsonp",
    jsonp: "callback", //傳遞給請求處理程序或頁面的,用以獲得jsonp回調函數名的參數名(一般默認為:callback)
    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屬性方法來調用,是不是很爽呀?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容