以前有一篇很老的文章網上轉了很多,包括現在如果你百度"跨域"這個關鍵字,前幾個推薦的都是"Javascript跨域總結與解決方案".看了一下感覺手段有點陳舊了,有一些比如document.domain還有iframe的解決方案委實"丑陋"一些,感覺不再適用於現在一些項目中.
就拿iframe來說作為一個前端工程師,我極為討厭iframe這種東西.它不光增加了性能上的高負荷,同時也不利於掌控.
在Angular應用中實現跨域的方式相對簡單,基本上通過兩種方式即可.一種是JSONP,另一種是通過CORS.前者是相對比較老的手法,後者我感覺更加給力一點,所以本文主要說一下Angular如何與CORS配合跨域.
能不使用JSONP就盡量不使用,這是著手於Angular跨域的一個原則吧.不管怎麼說,script的標簽嵌入感覺還是low了點.
Angular推崇的時前後端分離,所以跨域由哪一方實現成為一個問題.這個就不得不說前端技術上的局限性,即使是相對好用的JSONP對於非GET請求也是無能為力的,因為它本質上還是通過script去get一些資源.
JSONP這種只能GET的限制,在Angular推崇RESTful風格接口的API場景下,就完全制約了它的使用,總不能棄POST和PUT那些不管.並且JSONP的錯誤處理很弱,不盡人意.總之前端實現跨域都有各種各樣的局限性,又比如像document.domain則只能用於主域相同,子域不同的情況.
所以總結而言,雖然前端有多種方式處理跨域,但是多而不精,缺點都比較明顯.相對而言更好的方式是通過後端參與處理,這樣做不僅適用性更強,同時前端只要發送正常的Ajax請求即可.這樣的技術叫做CORS.
Cross-Origin Resource Sharing跨域資源共享,應該算是現在最為推薦的跨域處理方案.不僅適用於各種Method,而且更加方便和簡單.當然了,這麼吊的東西只有現代浏覽器支持,IE8一下的老古董就不要想了.
CORS實現原理
雖然通過CORS實現跨域基本上完全由後端實現,不過身為一個給力的前端.還是要掌握一下這一原理,以便當你遇到不靠譜的後端時,不至於...你懂得
CORS的本質讓服務器通過新增響應頭Access-Control-Allow-Origin,通過HTTP方式來實現資源共享,讓每個請求的服務直接返回資源.它使用了HTTP交互方式來確定請求源是否有資格請求該資源,並且通過設置HTTP Header來控制訪問資源的權限.
具體的過程是這樣的前端發送一個正常的請求:
$http.get('www.cros.com/api/data',{params:{
name: '頑Shi'
}})
後端設置一下response的header:
Access-Control-Allow-Origin: "*"
Access-Control-Allow-Methods: "GET"
Access-Control-Max-Age: "60"
然後你觀察一下浏覽器的行為會發現有趣的事,浏覽器在沒有你干預的情況下,發現這是一個跨域請求.所以它沒有直接發送GET請求,而是發送了一個OPTIONS請求詢問是否可以跨域訪問該資源,這個過程我們可以稱之為"預檢".
然後我們看到OPTIONS的response返回了類似下面的信息:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2013 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 60
Content-Encoding: gzip
Content-Length: 0
Connection: Keep-Alive
Content-Type: text/text
這裡的這幾個Access頭的內容就是服務器後端加上去的,它告訴了浏覽器此後的60秒內,所有域都可以通過GET方法進行跨域訪問該資源.然後浏覽器自動再次發送了真正的GET請求,並返回對應的結果.
注意這一過程是浏覽器自動實現的,這一點是不是非常棒.一些header信息的設置如下:
Access-Control-Allow-Origin: <origin> | * // 授權的源控制
Access-Control-Max-Age: <delta-seconds> // 授權的時間
Access-Control-Allow-Credentials: true | false // 控制是否開啟與Ajax的Cookie提交方式
Access-Control-Allow-Methods: <method>[, <method>]* // 允許請求的HTTP Method
Access-Control-Allow-Headers: <field-name>[, <field-name>]* // 控制哪些header能發送真正的請求
這裡還有一處需要前端工程師協作的地方就是cookie的傳遞,默認情況下通過CORS這樣的方式是不會傳遞cookie.一般強制性將cookie添加到header的做法,也會被浏覽器拒絕並報錯.上面看到了在服務器端會通過添加一個response頭,Access-Control-Allow-Credentials來控制是否允許Cookie的提交.
在Angular中我們需要進行一些設置達到目的:
$http.post(url, {withCredentials: true, ...})
// 或者
$http({withCredentials: true, ...}).post(...)
// 或者
.config(function ($httpProvider) {
$httpProvider.defaults.withCredentials = true;
}
如果是jQuery則要設置如下:
$.ajax("www.cros.com/api/data", {
type: "GET",
xhrFields: {
withCredentials: true
},
crossDomain: true,
success: function(data, status, xhr) {
}
});
CORS的過程描述完畢,在網上找到一張圖片:
CORS的分類
如果仔細觀察浏覽器的行為會發現,並不是所有的跨域請求都會發送OPTIONS請求.是不是有些奇怪,這就涉及到CORS的分類,簡單請求和復雜請求.
HTTP的header通常包含下面這些內容:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type的值僅是下列之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
HTTP方法是HEAD,GET,POST之一,同時HTTP的header包含如上面所示.任何一個不滿足這兩種要求的請求,都是復雜請求.比如發送PUT,DELETE等HTTP動作,或者Content-Type: application/json的內容.
只有復雜請求包含"預檢"這一動作,另外Access-Control-Max-Age應該也會影響OPTIONS請求的發送.
帶你走近AngularJS系列:
如何在 AngularJS 中對控制器進行單元測試 http://www.linuxidc.com/Linux/2013-12/94166.htm
在 AngularJS 應用中通過 JSON 文件來設置狀態 http://www.linuxidc.com/Linux/2014-07/104083.htm
AngularJS 之 Factory vs Service vs Provider http://www.linuxidc.com/Linux/2014-05/101475.htm
AngularJS —— 使用 ngResource、RESTful APIs 和 Spring MVC 框架提交數據 http://www.linuxidc.com/Linux/2014-07/104402.htm
AngularJS 的詳細介紹:請點這裡
AngularJS 的下載地址:請點這裡