先前的概念中對JSON仍是比較熟悉,對JSONP不是特別的清楚,整理完相關知識發現才豁然開朗。簡單的說JSON是一種數據交換格式,而JSONP是一種非官方跨域數據交互協議。JSON是「暗號」,而JSONP則是接頭方式。一個是描述信息的格式,一個是信息傳遞雙方約定的方法。javascript
1、什麼是JSONhtml
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。 易於人閱讀和編寫。同時也易於機器解析和生成。 它基於JavaScript Programming Language, Standard ECMA-262 3rd Edition – December 1999的一個子集。 JSON採用徹底獨立於語言的文本格式,可是也使用了相似於C語言家族的習慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 這些特性使JSON成爲理想的數據交換語言。java
JSON建構於兩種結構:jquery
這些都是常見的數據結構。事實上大部分現代計算機語言都以某種形式支持它們。這使得一種數據格式在一樣基於這些結構的編程語言之間交換成爲可能。git
JSON具備如下這些形式:github
對象是一個無序的「‘名稱/值’對」集合。一個對象以「{」(左括號)開始,「}」(右括號)結束。每一個「名稱」後跟一個「:」(冒號);「‘名稱/值’ 對」之間使用「,」(逗號)分隔。web
數組是值(value)的有序集合。一個數組以「[」(左中括號)開始,「]」(右中括號)結束。值之間使用「,」(逗號)分隔。ajax
值(value)能夠是雙引號括起來的字符串(string)、數值(number)、true
、false
、 null
、對象(object)或者數組(array)。這些結構能夠嵌套。編程
字符串(string)是由雙引號包圍的任意數量Unicode字符的集合,使用反斜線轉義。一個字符(character)即一個單獨的字符串(character string)。json
字符串(string)與C或者Java的字符串很是類似。
數值(number)也與C或者Java的數值很是類似。除去不曾使用的八進制與十六進制格式。除去一些編碼細節。
空白能夠加入到任何符號之間。 如下描述了完整的語言。
JSON的優勢:
JSON實例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
// 描述一我的
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;
|
2、什麼是JSONP?
JSONP(JSON with Padding)是資料格式 JSON 的一種「使用模式」,可讓網頁從別的網域要資料。因爲同源策略,通常來講位於 server1.example.com 的網頁沒法與不是 server1.example.com 的服務器溝通,而 HTML 的 <script>
元素是一個例外。利用<script>
元素的這個開放策略,網頁能夠獲得從其餘來源動態產生的 JSON 資料,而這種使用模式就是所謂的 JSONP。用 JSONP 抓到的資料並非 JSON,而是任意的 JavaScript,用 JavaScript 直譯器執行而不是用 JSON 解析器解析。
爲了理解這種模式的原理,先想像有一個回傳 JSON 文件的 URL,而 JavaScript 程式能夠用 XMLHttpRequest 跟這個 URL 要資料。假設咱們的 URL 是http://server2.example.com/RetrieveUser?UserId=xxx 。假設小明的 UserId 是 1823,且當瀏覽器透過 URL 傳小明的 UserId,也就是抓取http://server2.example.com/RetrieveUser?UserId=1823 ,獲得:
1
|
{"Name": "小明", "Id" : 1823, "Rank": 7}
|
這個 JSON 資料多是依據傳過去 URL 的查詢參數動態產生的。
這個時候,把 <script>
元素的 src 屬性設成一個回傳 JSON 的 URL 是能夠想像的,這也表明從 HTML 頁面透過 script 元素抓取 JSON 是可能的。
然而,一份 JSON 文件並非一個 JavaScript 程式。爲了讓瀏覽器能夠在 <script>
元素執行,從 src 裏 URL 回傳的必須是可執行的 JavaScript。在 JSONP 的使用模式裏,該 URL 回傳的是由函數呼叫包起來的動態生成 JSON,這就是JSONP 的「填充(padding)」或是「前輟(prefix)」的由來。
慣例上瀏覽器提供回調函數的名稱看成送至服務器的請求中命名查詢參數的一部份,例如:
1
|
<script type="text/javascript" src="http://server2.example.com/RetrieveUser?UserId=1823&jsonp=parseResponse"> </script>
|
服務器會在傳給瀏覽器前將 JSON 數據填充到回調函數(parseResponse)中。瀏覽器獲得的迴應已不是單純的資料敘述而是一個腳本。在本例中,瀏覽器獲得的是:
1
|
parseResponse({"Name": "Cheeso", "Id" : 1823, "Rank": 7})
|
雖然這個填充(前輟)「一般」是瀏覽器執行背景中定義的某個回調函數,它也能夠是變量賦值、if 敘述或者是其餘 JavaScript 敘述。JSONP 要求(也就是使用 JSONP 模式的請求)的迴應不是 JSON 也不被看成 JSON 解析——回傳內容能夠是任意的運算式,甚至不須要有任何的 JSON,不過慣例上填充部份仍是會觸發函數調用的一小段 JavaScript 片斷,而這個函數呼叫是做用在 JSON 格式的資料上的。
另外一種說法—典型的 JSONP 就是把既有的 JSON API 用函數呼叫包起來以達到跨域存取的解法。爲了要啓動一個 JSONP 呼叫(或者說,使用這個模式),你須要一個 script 元素。所以,瀏覽器必須爲每個 JSONP 要求加(或是重用)一個新的、有所需 src 值的 <script> 元素到 HTML DOM 裏—或者說是「注入」這個元素。瀏覽器執行該元素,抓取 src 裏的 URL,並執行回傳的 JSON。也由於這樣,JSON 被稱做是一種「讓使用者利用 script 元素注入的方式繞開同源策略」的方法。
使用遠端網站的 script 標籤會讓遠端網站得以注入任何的內容至網站裏。若是遠端的網站有 JavaScript 注入漏洞,原來的網站也會受到影響.如今有一個正在進行計劃在定義所謂的 JSON-P 嚴格安全子集,使瀏覽器能夠對 MIME 類別是「application/json-p」請求作強制處理。若是迴應不能被解析爲嚴格的 JSON-P,瀏覽器能夠丟出一個錯誤或忽略整個迴應。
粗略的 JSONP 部署很容易受到跨網站的僞造要求(CSRF/XSRF)的攻擊。由於 HTML <script> 標籤在瀏覽器裏不遵照同源策略,惡意網頁能夠要求並取得屬於其餘網站的 JSON 資料。當使用者正登入那個其餘網站時,上述情況使得該惡意網站得以在惡意網站的環境下操做該 JSON 資料,可能泄漏使用者的密碼或是其餘敏感資料。
只有在該 JSON 資料含有不應泄漏給第三方的隱密資料,且服務器僅靠瀏覽器的同源策略阻擋不正常要求的時候這纔會是問題。若服務器本身決定要求的專有性,並只在要求正常的狀況下輸出資料則沒有問題。只靠 Cookie 並不夠決定要求是合法的,這很容易受到跨網站的僞造要求攻擊。
JSONP的客戶端具體實現:
下面來講明一下jsonp在客戶端的實現:
一、咱們知道,哪怕跨域js文件中的代碼(固然指符合web腳本安全策略的),web頁面也是能夠無條件執行的。
遠程服務器remoteserver.com根目錄下有個remote.js文件代碼以下:
1
|
alert('我是遠程文件');
|
本地服務器localserver.com下有個jsonp.html頁面代碼以下:
1
2
3
4
5
6
7
8
9
|
<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>
|
毫無疑問,頁面將會彈出一個提示窗體,顯示跨域調用成功。
二、如今咱們在jsonp.html頁面定義一個函數,而後在遠程remote.js中傳入數據進行調用。
jsonp.html頁面代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<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的服務者都要面對不少服務對象,而這些服務對象各自的本地函數都不相同啊?咱們接着往下看。
聰明的開發者很容易想到,只要服務端提供的js腳本是動態生成的就好了唄,這樣調用者能夠傳一個參數過去告訴服務端「我想要一段調用XXX函數的js代碼,請你返回給我」,因而服務器就能夠按照客戶端的需求來生成js腳本並響應了。
看jsonp.html頁面的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<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(服務端的實現這裏就不演示了,與你選用的語言無關,說到底就是拼接字符串):
1
2
3
4
5
|
flightHandler({
"code": "CA1998",
"price": 1780,
"tickets": 5
});
|
咱們看到,傳遞給flightHandler函數的是一個json,它描述了航班的基本信息。運行一下頁面,成功彈出提示窗口,jsonp的執行全過程順利完成!
四、到這裏爲止的話,相信你已經可以理解jsonp的客戶端實現原理了吧?剩下的就是如何把代碼封裝一下,以便於與用戶界面交互,從而實現屢次和重複調用。
jQuery對JSONP的實現
咱們依然沿用上面那個航班信息查詢的例子,假定返回jsonp結果不變:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<!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時自動幫你生成回調函數並把數據取出來供success屬性方法來調用。
ajax與jsonp的異同
總而言之,jsonp不是ajax的一個特例,哪怕jquery等巨頭把jsonp封裝進了ajax,也不能改變着一點!
參考文檔: