對于JSON和JSONP,應該都不陌生,咳咳,不過最初對JSONP有點誤解,以為是JSON的另外一個別名,其實二者風馬牛不相及。
談到Json和jsonp就不可避免要提到跨域這個話題了,以前對跨域的理解都是比較模糊的,還以為域名不同就是跨域了,那太狹隘了。非同源請求,均為跨域。
不過為什么會出現跨域?出于瀏覽器的同源策略限制,瀏覽器會拒絕跨域請求。
嚴格的說,瀏覽器并不是拒絕所有的跨域請求,實際上拒絕的是跨域的讀操作。瀏覽器的同源限制策略是這樣執行的:
通常瀏覽器允許進行跨域寫操作(Cross-origin writes),如鏈接,重定向;
通常瀏覽器允許跨域資源嵌入(Cross-origin embedding),如 img、script 標簽(主要是有src);
通常瀏覽器不允許跨域讀操作(Cross-origin reads)。*
等等,上面咋又來了個同源策略,本是同根生,相煎何太急啊。
大家互相開開心心的走親訪友多好。哼,誰知道你是我親戚還是壞人,萬一你來我家是想偷小魚干的呢?還開開心心,本喵不得哭死啊。 同源策略 (Same-Origin Policy) 最早由 Netscape 公司提出, 所謂同源就是要求, 域名, 協議, 端口相同. 非同源的腳本不能訪問或者操作其他域的頁面對象(如DOM等). 作為著名的安全策略, 雖然它只是一個規范, 并不強制要求, 但現在所有支持 javaScript 的瀏覽器都會使用這個策略. 以至于該策略成為瀏覽器最核心最基本的安全功能, 如果缺少了同源策略, web的安全將無從談起.(這段文字是cv的)
這下好了,同源策略下的web世界, 域的壁壘高筑, 保證各個網頁相互獨立, 互相之間不能直接訪問, iframe, ajax 均受其限制, 而script標簽不受此限制.
注: 如非特別說明, 均指非CORS的, 普通跨域請求.
咳咳,我們講json呢,扯遠了,快回來
哎,哎,相公,別敲我腦袋瓜子啊,疼,敲笨了你就只能有個笨媳婦。人家這不是麻溜的回來了嘛,你倒是給我說說json和驚悚有啥不同。
“你知道啥是json么?”
“本大喵當然知道,json是一種數據格式”
“手寫一段給本汪瞅瞅”
// 描述一個人
var person = {
"Name": "大寶",
"Age": 1,
"Company": "IBM",
"Engineer": true
}
“算你上次沒逃課,那你給我說說,這個json有啥要注意的地方?”
“咦,json不就簡單的數據格式嗎,有啥要注意?”
“就知道你上次沒認真聽,肯定開小差了,今晚回去小魚干沒了?!?/p>
“喵嗚~~~人家錯了,你再說一遍吧?嚶嚶嚶”
“記得下次考你,看仔細了”
■ ■■■■
這會兒帶你認識認識jsonp了,看會兒驚悚片
“喵喵,你知道ajax么?”
“聽過,但是不太了解”
“推薦一個blog,你去看看,下次講給我聽聽,答的好有小魚干吃喲”
“猴!得令”
Ajax直接請求普通文件存在跨域無權限訪問的問題,無論是靜態頁面還是動態頁面,web服務,WCF(喵嗚,這是啥?),但是在web頁面上調用js文件時不受到跨域的影響(凡是擁有src屬性的都有跨域的神奇能力),所以可以通過在遠程服務器上設法把數據裝進js格式的文件里,供客戶端調用和進一步處理,而處理這些數據的格式可以是json,而且json還被原生js支持,很完美了。
方案如下:
Web客戶端通過與調用腳本一模一樣的方式,來調用跨域服務器上動態生成的js格式文件,客戶端在對json文件成功調用之后,獲得了自己所需的數據,這就是jsonp,該協議的一個要點就是允許用戶傳遞一個callback參數給服務端,然后服務端返回數據時會將這個callback參數作為函數名來包裹住json數據,這樣客戶端就可以隨意定制函數來自動處理返回數據。
具體實現示例:
喵喵:遠程服務器。cat.com
汪汪:本地服務器.dag.com
1 miaomiao.js是cat.com根目錄下的一個js文件。 代碼如下
<pre style="margin: 0.5em 0px; padding: 0.4em 0.6em; border-radius: 8px; background: rgb(248, 248, 248); box-sizing: border-box;">alert(‘喵嗚,我是喵喵’);</pre>
Jsonp.html是dag.com下的一個頁面:
這里會彈出彈窗,現實跨域成功。
2 在jsonp.html頁面定義一個函數。然后在遠程文件miaomiao.js中傳入數據進行調用。
miaomiao.js:
localHandler({"result":"我是遠程貓js帶來的數據"});
運行之后,顯示本地調用成功,并且獲取數據。但是如何讓miaomiao知道它調用的dog函數叫什么呢?畢竟附近的dog太多了。
3 喵喵和汪汪想了一個辦法,如果汪汪想要調用喵喵,就在返回的骨頭上加一個標志,說我想調用XXX函數的js代碼,你丫別給我傳錯了啊。于是喵喵就按照骨頭上的需求來生成js腳本并且給汪汪一個響應“拿好你的骨頭,別搞丟了” 汪汪的jsonp.html
上面實現的是編碼動態查詢,也是jsonp客戶端實現的核心。
下面是如何完成jsonp調用的全過程。 上面url中的code參數表示dog告訴cat我要查詢附近豬骨的信息,并且把一個叫callback的骨頭給cat,說這是我們的暗號(boneHandler),別和其他狗子搞混了,你把結果都放到這塊骨頭的這個暗號中給我傳過來。
于是這個叫做boneResult.aspx的頁面生成了一段這樣的代碼提供給jsonp.html
boneHandler({
"code": "pig",
"price": 170,
"nums": 5
});
4 如何用jquery實現? Attention:jquery在處理jsonp類型的ajax時(雖然jquery也把jsonp歸入了ajax,但其實它們真的不是一回事兒),自動幫你生成回調函數并把數據取出來供success屬性方法來調用
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,也不能改變著一點!
“傻喵,聽懂了么?”
參考資料:
路易斯的blog(推薦看看他的blog,內容滿贊的,尤其那個關于mac上使用alfred的技巧,很清晰)
阮一峰的日志
我的博客即將搬運同步至騰訊云+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=1eteae3c4sdc0