什么是CORS?
默認情況下,為預防某些而已行為,瀏覽器的XHR對象只能訪問來源於同一個域中的資源。但是我們在日常實際開發中,常常會遇到跨域請求的需求,因此就出現了一種跨域請求的方案:CORS(Cross-Origin Resource Sharing)跨域資源共享。
CORS背后的原理是:使用自定的HTTP頭部與服務器進行溝通,從而由服務器決定響應是否成功。
如何使用CORS?
使用CORS需要客戶端和服務端兩者配合。
一、客戶端如何發起CORS跨域請求?
目前在大多數瀏覽器下(CORS在各瀏覽器下支持情況),都原生支持CORS,代碼編寫時和同域的請求差不多,只需要在xhr.open()的時候傳入絕對URL即可。例如:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
console.log(xhr.responseText)
}else {
console.log('err' + xhr.status);
}
}
};
xhr.open('get','http://www.xxx.com/api/something/',true);
xhr.send(null);
這樣就可以發送一個跨域請求了,但是如果只是如上面示例代碼一樣發送的話會報錯,因為服務器並未設置允許我們這個請求,因此CORS還需要服務端來配合。
二、服務器如何允許客戶的CORS跨域請求?
服務器只需要在響應頭部中設置Access-Control-Allow-Origin即可讓客戶端訪問。
假設客戶端的域名是http://www.xxx.com,那么服務端只要在Access-Control-Allow-Origin的設置中含有http://www.xxx.com,那么這個CORS請求即可成功。如果Access-Control-Allow-Origin設置為*,那么任意域名都可以訪問這個服務端,但是為了安全起見,一般並不建議這樣做。
以下截圖是一個CORS請求后服務端正常返回的示例:
Preflighted Request
CORS還有一種叫做Preflighted Request(預飛請求)的透明服務器驗證機制完成請求過程,如果你在請求的時候使用了表1中的選項來發送請求(使用setRequestheaders設定自定義頭部),那么就會觸發Preflighted Request,它的請求過程如下:
1.XHR對象send發出請求
2.瀏覽器先向服務端發出一個OPTIONS方法的請求,並發送下列頭部:
- 表1
請求頭部信息 | 含義 |
---|---|
Origin | 來源域名,與簡單的請求相同。 |
Access-Control-Request-Method | 請求自身使用的方法。 |
Access-Control-Request-Headers (可選) | 自定義的頭部信息,多個頭部以逗號分隔。 |
OPTIONS請求示例:
客戶端請求的代碼(比上面多加了個header):
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
console.log(xhr.responseText)
}else {
console.log('err' + xhr.status);
}
}
};
xhr.open('get','http://www.xxx.com/api/poisearch/',true);
xhr.setRequestHeader('haha',1);
xhr.send(null);
請求結果(這里OPTIONS請求觸發了,但沒有找到這個url):
3.服務器接收到這個請求后,根據上面的頭部信息判斷是否予以接收。並在響應中發送如下頭部與瀏覽器進行溝通:
- 表2
響應頭部信息 | 含義 |
---|---|
Access-Control-Allow-Origin | 來源域名,與簡單的請求相同。 |
Access-Control-Allow-Methods | 允許的方法,多個方法以逗號分隔。 |
Access-Control-Allow-Headers(可選) | 允許的頭部,多個頭部以逗號分隔。 |
Access-Control-Max-Age | 應該將這個Preflight請求緩存多長時間(以秒表示) |
4.Preflighted Request結束后,結果將按照指定的時間緩存起來。
5.如果服務端判斷上面設置的額外信息可以允許請求,那么就會再請求一次正常的請求了。