JSONP - 從理論到實踐
同源策略
ajax之所以需要“跨域”,罪魁禍首就是瀏覽器的同源策略。即,一個頁面的ajax只能獲取這個頁面相同源或者相同域的數據。
如何叫“同源”或者“同域”呢?——協議、域名、端口號都必須相同。例如:
- http://google.com 和 https://google.com 不同,因為協議不同;
- http://localhost:8080 和 http://localhost:1000 不同,因為端口不同;
- http://localhost:8080 和 https://google.com 不同,協議、域名、端口號都不同,根本不是一家的。
根據同源策略,我自己做的一個網頁 http://localhost:8080/test.html 就無法通過ajax直接獲取 http://google.com 的數據。
例如,我用ajax去訪問一個不同域的頁面,錯誤結果是這樣的:
大家想想,這樣其實也有道理。如果沒有同源策略,你我都可以隨便通過ajax直接獲取其他網站的信息,這還不亂套了。。。我自己做一個搜索界面,搜索時直接用ajax從百度獲取數據,那不成了小偷了。。。
但是跨域訪問是少不了的,mail.163.com 的網頁可能需要從 news.163.com 域下獲取新聞信息,那怎麽辦?——開始咱們的跨域之旅。(當然用iframe也可以實現)
從“盜鏈”說起
互聯網的許多網站之間圖片相互盜鏈,A網站網頁的img.src直接鏈接到B網站的圖片地址,這是常有的事兒。說到“盜鏈”,大家第一想到的可能是如何去防止盜鏈,今兒咱不管那個。
你再想想“盜鏈”和“同源策略”這兩個詞之間有什麽關系?——對,矛盾!既然都“同源策略”了,怎麽還能“盜鏈”呢?
世間萬物都有矛盾,有矛盾了照樣可以和諧共處,並不一定非要你死我活。
重點:<img>
的src
(獲取圖片),<link>
的href
(獲取css),<script>
的src
(獲取javascript)這三個都不符合同源策略,它們可以跨域獲取數據。因此,你可以直接從一些cdn上獲取jQuery,並且你網站上的圖片也隨時可能被別人盜用,所有最好加上水印!
而我們今天的主角——jsonp——就是因為<script>的src不符合同源策略而來的。
JSONP
例如,域名 a.com 下有一個 a.com/test.html 網頁,域名 b.com 下有一個 b.com/data.html 網頁和 b.com/alert.js 文件。
引導第一步:簡單引用js
編寫 b.com/alert.js 如下:
alert(123);
對 a.com/test.html 編寫如下代碼:
<script type=‘text/javascript‘ src=‘http://b.com/alert.js‘/>
運行 a.com/test.html,結果很明顯,就是彈出123
引導第二步:引用js返回數據
將 b.com/alert.js 修改為:
myFn(100);
將 a.com/test.html 修改為:
<script>
function myFn ( data ) {
alert( data + ‘px‘ );
}
</script>
<script type=‘text/javascript‘ src=‘http://b.com/alert.js‘/>
運行 a.com/test.html,結果是彈出100px
,這個應該也沒有什麽疑問。
引導第三步:已經跨域成功!
第二步中,如果data——即100
——是我要跨域在b.com下獲取的一個數據,那麽咱們這不就是已經實現跨域請求了嗎!!!
把這個過程再清晰的捋一遍:
<script>
的src
不符合同源策略;- 我通過給
<script>
的src
賦值一個跨域的文件的網址(可能不是一個js文件),這個文件返回的字符串,瀏覽器會當作javascript來解析; - 而這段javascript中,就可以包含著我所需要的跨域服務器端的數據;
- 最後,我在本頁面定義一個myFn函數用來展示數據,而這段javascript中就可以直接調用myFn函數;
引導第四步:引用html格式
<script>
的src
不一定僅僅指向javascript文件,可以指向任何地址。例如:
將 a.com/test.html 修改為:
<script>
function myFn ( data ) {
alert( data + ‘px‘ );
}
</script>
<script type=‘text/javascript‘ src=‘http://b.com/data.html‘/>
將 b.com/data.html 編寫為:(註意,data.html中就寫以下一行代碼,多了不寫)
myFn(100);
運行 a.com/test.html ,結果依然是100px
,其中,100
就是我們要跨域請求的數據。
引導第五步:動態數據
如果要請求的數據是動態的,那就要在動態頁面中編寫。那麽我們就讓 a.com/test.html 去調用一個動態的aspx頁面:
<script>
function myFn ( data ) {
alert( data + ‘px‘ );
}
</script>
<script type=‘text/javascript‘ src=‘http://b.com/data.aspx?callback=myFn‘/>
大家註意,我們在 src 地址中增加了?callback=myFn
,意思是把顯示數據的函數也動態傳過去了,而第二步、第四步都是靜態的寫在被調用的文件中的。至於callback參數後臺如何接收,如何使用,請接著看:
在 b.com 下增加一個 b.com/data.aspx 頁面,後臺代碼如下:
protected void Page_Load(object sender, EventArgs e)
{
if (this.IsPostBack == false)
{
string callback = "";
if (Request["callback"] != null)
{
callback = Request["callback"];
//服務器端要返回的數據
string data = "1024";
Response.Write(callback + "(" + data + ")");
}
}
}
代碼很簡單,獲取callback參數,然後組成一個函數的形式返回。如果b.com/data.aspx?callback=myFn
調用的話,那麽返回的就是myFn(1024)
。
返回的數據變成動態的了(“1024”),前端頁面用於顯示數據的函數也編程了動態的了(“callback=myFn”),但是歸根結底,形式還是一樣的。
引導第六步:調用封裝
a.com/test.html 中,僅僅有一個<script>
靜靜的躺在那裏,執行一次之後,就沒有作用了。
而實際情況是,a.com/test.html 中,可能隨著用戶的操作發生若幹次的調用。怎麽辦?——動態增加唄。
function addScriptTag(src) {
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.src = src;
document.body.appendChild(script);
}
function myFn (data) {
alert(data + ‘px‘);
}
//需要調用時:
//addScriptTag(‘b.com/data.aspx?callback=myFn‘);
總結
以上層層描述的就是JSONP,你不必去記住它的定義,看明白了上述文字,就全能理解。
重點在於:同源策略 + <script>
的src
不屬於同源策略 + 通過<script>
的src指向的文件返回服務器端數據。
ok,就這些!
作者: 王福朋
鏈接:http://www.imooc.com/article/18739
來源:慕課網
本文原創發布於慕課網 ,轉載請註明出處,謝謝合作!
JSONP - 從理論到實踐