一、爲什麼要跨域
跨域,通常情況下是說在兩個不通過的域名下面無法進行正常的通信,或者說是無法獲取其他域名下面的數據,這個主要的原因是,瀏覽器出於安全問題的考慮,採用了同源策略,通過瀏覽器對JS的限制,防止惡意用戶獲取非法的數據。比如這樣的一個場景,惡意用戶仿造一個銀行的官網,在用戶輸入框中嵌套了銀行的頁面,如果是沒有同源策略的限制,那麼惡意用戶則可以通過這樣的一種方法來獲取銀行用戶的卡號和登錄密碼,這樣對於瀏覽器來說是沒有安全性可言的。同時也可以有效的規避了大部分的XSS攻擊(XSS攻擊原理:通過向用戶界面中注入script腳本,然後在腳本中獲取用戶的token等身份信息,然後將身份信息發送到惡意用戶指定的地方,在正常用戶還沒有推出的時候,也就是token等身份信息還有效的時候,通過這些信息強制登錄,將正常用戶擠下系統。)
同源定義:即同一域,即相同協議&相同端口&相同域名&相同子域名
同源策略規定:XHR對象只能訪問與包含它的頁面位於同一域中的資源,有利於預防一些惡意行爲。
二、怎麼解決跨域
解決辦法有很多,CORS、iframe、h5新特性postMessage等,而比較簡單的方法就是今天着重介紹的jsonp。
解決依據:儘管不能訪問非本域的動態資源,但是類似js文件、樣式、圖片等靜態資源是可以訪問的!就是通過這個“漏洞”來解決跨域問題。用<script>標籤中的src來寫入跨域數據的url,這樣就能繞過同源策略了。。“老師,他作弊!”
三、簡單介紹jsonp
JSONP,JSON with Padding的簡寫,這個全稱對jsonp的理解還是有一定的幫助的。填充式JSON或者說是參數式JSON。JSONP的語法和JSON很像,簡單來說就是在JSON外部用一個函數包裹着。JSONP基本語法如下:
callback({ "name": "kwan" , "msg": "獲取成功" });
JSONP兩部分組成:回調函數和裏面的數據。回調函數是當響應到來時,應該在頁面中調用的函數,一般是在發送過去的請求中指定。
JSONP原理:
剛纔的解決依據可知,JSONP原理就是動態插入帶有跨域url的<script>標籤,然後調用回調函數,把我們需要的json數據作爲參數傳入,通過一些邏輯把數據顯示在頁面上。
四、JSONP跨域小實踐
你再怎麼懂原理,再怎麼會JSONP基本語法,不真正執行一下你其實還是懵逼。
當然,我認爲我還是需要模擬不同源的跨域請求才對真正弄懂跨域有一定好處。
下面展示的代碼源於那篇文章,需求是輸入歌名,點擊搜索後,跨域請求API接口,返回數據後,顯示專輯名在頁面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP</title>
</head>
<body>
<input type="text" id="song" name="">
<input type="button" id="song_search" value="歌曲搜索" name="">
<br />
<div style="width:200px;height:230px;background:pink" id="song_list"></div>
<script type="text/javascript" src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
var searchJsonCallback=function(data){
//遍歷查詢結果
var alb_html='';
for(var i in data.list){
alb_html+='<span>專輯:</span><div style="color:black">'+data.list[0].albumname+'</div>';
}
$("#song_list").html(alb_html);
};
$("#song_search").on("click",function(){
var keyword=$("#song").val();
if(keyword==undefined||keyword==""){
alert("歌曲搜索不能爲空");
return false;
}else{
var url = "http://cgi.music.soso.com/fcgi-bin/fcg_search_xmldata.q?source=10&w="+keyword+"&perpage=1&ie=utf-8";
// 創建script標籤,設置其屬性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script標籤加入head,此時調用開始
document.getElementsByTagName('head')[0].appendChild(script);
}
});
</script>
</body>
</html>
下面是我對以上代碼的一些文字解釋:
1)點擊按鈕後動態插入跨域數據,然後由於此接口用searchJsonCallback({})來封裝json格式數據,因此可看作是調用一個函數,同時把json數據作爲參數傳入
2)所以當動態插入script標籤後,寫好了的searchJsonCallback()函數將被調用,參數data就是json數據,然後通過遍歷渲染到DOM上,完成整個跨域獲取數據流程
總結步驟:觸發click事件-動態插入帶有API接口的script標籤-根據回調函數名調用函數-遍歷數據-渲染到頁面
五、當JSONP遇上jQuery ajax
注意了,千萬不要認爲用了jQuery ajax,就是通過它來實現跨域請求,其實只是因爲它很好地封裝了JSONP而已
用jQuery ajax原理和上面的是一樣的,只不過我們不需要手動的插入script標籤以及定義回調函數。jquery會自動生成一個全局函數來替換callback=?中的問號,之後獲取到數據後又會自動銷燬,實際上就是起一個臨時代理函數的作用。
於是對上面的代碼進行修改,並且把請求數據改爲每次顯示5條:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP2</title>
</head>
<body>
<input type="text" id="song" name="">
<input type="button" id="song_search" value="歌曲搜索" name="">
<br />
<div style="width:200px;height:230px;background:pink" id="song_list"></div>
<script type="text/javascript" src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
$("#song_search").on("click",function(){
var keyword=$("#song").val();
if(keyword==undefined||keyword==""){
alert("歌曲搜索不能爲空");
return false;
}else{
$.ajax({
url: 'http://cgi.music.soso.com/fcgi-bin/fcg_search_xmldata.q?source=10&w='+keyword+'&perpage=5&ie=utf-8',
type: 'GET',
dataType: 'jsonp',
jsonp:'callback',
jsonpCallback: 'searchJsonCallback',
success:function(data){
//遍歷查詢結果
var alb_html=''; //創建一個變量用於DOM拼接
for(var i in data.list){
alb_html+='<p style="color:black">'+data.list[i].albumname+'</p>';
console.log(data.list[i].singername);
}
var name_html='<span>專輯:</span>';
var sum_html=name_html+alb_html;
$("#song_list").html(sum_html);
}
});
}
});
</script>
</body>
</html>
關於jsonp、jsonpCallback兩個屬性,部分解釋一直沒看懂,如下:
jsonp: "callback",
//傳遞給請求處理程序或頁面的,用以獲得jsonp回調函數名的參數名(一般默認爲:callback)
jsonpCallback:"handler",
//自定義的(?)jsonp回調函數名稱,默認爲jQuery自動生成的隨機函數名,也可以寫"?",jQuery會自動爲你處理數據
直到有次看了下Network,懂了。其實就是拼接到url時想要顯示的內容。
如上面情況,url則會變成"http://...&perpage=5&ie=utf-8&callback=searchJsonCallback&..."
所以簡單來說,
jsonp就是相當於一個名字,一個參數名
jsonpCallback就是回調函數名,用來包裹JSON數據的
然後將jsonpCallback定義的函數賦值給jsonp定義的參數上,
最後拼接在url末尾完成參數傳遞。
注意:jsonp可以隨便寫,不寫也行,反正默認是callback
但jsonpCallback必須是對應其返回數據的函數名,此API接口返回的數據外圍用searchJsonCallback()包裹數據,因此只能是這個
(有些文章說這個也是可以自定義的,但我測試時隨便寫就報錯了。。可能因爲這個API接口固定了函數名吧)
其實上面這些例子更方便將理論用於實踐,畢竟數據等都是現成的,跨域問題也是常見的開發問題,感覺就沒必要大費周章才能進行模擬跨域(所以我上面說自己弄服務器那種方法略麻煩)
轉自:https://segmentfault.com/a/1190000009577990
代碼出自:http://www.cnblogs.com/st-les...
另一篇幫助理解的文章:http://blog.csdn.net/u0139455...