要記錄好這個(gè)問(wèn)題,首先需要描述清楚這個(gè)問(wèn)題。
有如下的一個(gè)場(chǎng)景:我們?cè)趯?shí)現(xiàn)一個(gè)系統(tǒng)中某個(gè)功能的時(shí)候,可能系統(tǒng)的某些模塊不在一個(gè)域名下。用戶中心模塊部署在user.xxx.com下,產(chǎn)品相關(guān)的功能部署在product.xxx.com下,在用戶中心的某些頁(yè)面,我們或許要請(qǐng)求產(chǎn)品功能模塊的功能,此時(shí)就會(huì)有問(wèn)題出現(xiàn)。以Google Chrome(版本 45.0.2454.101 m)瀏覽器為例,當(dāng)當(dāng)前的頁(yè)面在A域名下,我們?cè)陧?yè)面中構(gòu)造了一個(gè)異步請(qǐng)求,我們需要調(diào)用位于B域名下的某個(gè)API,代碼片段如下:
<script type="text/javascript">
$(function() {
//當(dāng)前頁(yè)面所在域名:http://localhost:9000/
//jQuery版本:jquery-1.11.3.min.js
$.ajax({
url : "http://localhost:9001/order/pindexpost",
type : "POST",
dataType : 'json',
success : function(data) {
alert(data);
}
});
});
</script>
但是請(qǐng)求之后,瀏覽器控制臺(tái)給出了如下的錯(cuò)誤信息
那么這就是標(biāo)題中所提到的跨域資源共享CORS問(wèn)題了。我們把這個(gè)概念細(xì)化一下,首先,為什么會(huì)有CORS?簡(jiǎn)單點(diǎn)理解就是出于安全考慮,瀏覽器都遵循一個(gè)叫同源策略的東西,他約束瀏覽器說(shuō)“不是你的東西,你問(wèn)人家要,人家默認(rèn)不給你,除非人家同意給你”,基于這個(gè)策略,就有了跨域資源共享的問(wèn)題,那么,怎么就叫跨域了呢?上面代碼中的域名不是一樣的嗎,都是指向本地的localhost啊。舉個(gè)栗子說(shuō)明下
http://www.tkk7.com:80,左邊是一個(gè)完整的請(qǐng)求,包含了協(xié)議,子域名等等若干部分,我們把它理解為域,但凡是在這個(gè)域下發(fā)生的請(qǐng)求,都無(wú)法直接訪問(wèn)到其他域的資源
- http://www.tkk7.com:80能訪問(wèn)http://www.tkk7.com:81下的資源嗎?不行!
- http://www.tkk7.com:80能訪問(wèn)http://abc.blogjava.net:80下的資源嗎?不行!
- http://www.tkk7.com:80能訪問(wèn)http://www.tkk7.com:80下的資源嗎?不行!
- http://www.tkk7.com:80能訪問(wèn)http://域名對(duì)應(yīng)的真實(shí)IP:80下的資源嗎?不行!
- http://www.tkk7.com:80能訪問(wèn)http://www.xyz.net:80下的資源嗎?不行!
域哪怕有一點(diǎn)點(diǎn)不一樣,一點(diǎn)點(diǎn)不一樣,一點(diǎn)點(diǎn)不一樣,都是不可以訪問(wèn)的,那么問(wèn)題來(lái)了,如何實(shí)現(xiàn)跨域呢? 目前有三種方案可以打破同源策略所帶來(lái)的限制,實(shí)現(xiàn)跨域請(qǐng)求。
-
使用JSONP。這種方案是使用HTML的script標(biāo)簽來(lái)實(shí)現(xiàn)的,script標(biāo)簽的src屬性不受同源策略約束,可以訪問(wèn)任意站點(diǎn)的資源,但是,該方案有著自己的約束。首先,他只能發(fā)出GET請(qǐng)求,因?yàn)閟cript標(biāo)簽的初衷就是為了獲取js腳本,所以如果返回的數(shù)據(jù)內(nèi)容不是js腳本的話,也不會(huì)成功。基于這個(gè)約束,我們需要改造一下我們的請(qǐng)求:
1 <script type="text/javascript">
2 $(function () {
3 //當(dāng)前頁(yè)面所在域名:http://localhost:9000/
4 // jQuery版本:jquery-1.11.3.min.js
5 $.ajax({
6 url: "http://localhost:9001/order/pindexpost",
7 type: "POST", dataType: 'jsonp',
8 jsonpCallback: "callback"
9 });
10 })
11 ;
12 function callback(result) {
13 alert("suc:" + result);
14 }
15 </script>
剛才提到,JSONP方式是基于script標(biāo)簽的,所以我們要對(duì)返回的數(shù)據(jù)做一些處理,使返回的數(shù)據(jù)是一段js腳本才可以。我們?cè)诳蛻舳祟A(yù)定義好callback函數(shù),使得服務(wù)端返回?cái)?shù)據(jù)的時(shí)候可以直接調(diào)用這個(gè)callback函數(shù),服務(wù)端代碼片段如下:
1 response().setContentType("application/javascript");
2 return ok("callback(" + data + ")");
要注明返回的格式,并且data的格式也要符合js的語(yǔ)法。其實(shí)說(shuō)白了,JSONP就是請(qǐng)求目標(biāo)數(shù)據(jù),然后將目標(biāo)數(shù)據(jù)和回調(diào)函數(shù)進(jìn)行拼接,以javascript的形式返回給瀏覽器,之后瀏覽器執(zhí)行的這個(gè)回調(diào)函數(shù)。
使用代理服務(wù)器。這個(gè)比較好理解,不過(guò)運(yùn)維的成分多一些,設(shè)置一個(gè)代理服務(wù)器,根據(jù)請(qǐng)求API所在命名空間的不同,轉(zhuǎn)發(fā)到相應(yīng)的域去,這樣一來(lái)就騙過(guò)了瀏覽器,讓瀏覽器以為所有的數(shù)據(jù)都來(lái)自一個(gè)域。
- 設(shè)置請(qǐng)求的相應(yīng)頭。在目標(biāo)API的響應(yīng)頭中添加“Access-Control-Allow-Origin”,設(shè)置值為“*”,這就是讓服務(wù)器通知瀏覽器,說(shuō)“我這個(gè)API的響應(yīng),是面向所有人的,*就代表這個(gè)意思啊”,但是,這也會(huì)帶來(lái)一個(gè)安全性的問(wèn)題,所以你可以設(shè)置部分站點(diǎn)可以跨域訪問(wèn),比如Access-Control-Allow-Origin: http://www.tkk7.com/,但是這個(gè)響應(yīng)頭存在一定的兼容問(wèn)題,具體支持情況可以查看caniuse.com