前面的話
前端日問,鞏固基礎,不打烊!!!
解答
之前小柒也總結過九種跨域方法(裏面有相關實例),這篇文章參考阮一峯老師的文章詳細梳理一下cors。
簡述
cors - 跨域資源共享,需要瀏覽器和服務器同時支持, 這種通信方式原理其實就是通過自定義http的頭部來進行通信。
兩種請求
- 簡單請求:比如
POST
、GET
、HEAD
- 非簡單請求:比如
PUT
、DELET
或者Content-Type
字段類型爲application/json
簡單請求
如果是簡單請求的,瀏覽器直接發出CORS請求,會自動帶上Origin
字段(指定請求來自哪個域名)。
當服務端收到響應後:
-
如果
Origin
指定的源不在服務器允許的範圍內,那麼服務端會返回一個正常的HTTP
迴應。但是這個響應是不帶Access-Control-Allow-Origin
字段的,瀏覽器發現響應頭沒有包含這個字段,就知道出錯了,就會拋出一個錯誤。(這個錯誤是由XMLHTTPRequest的onerror回調函數捕獲的)。 -
如果
Origin
指定的源在服務器允許的範圍內,那麼服務端返回的響應頭就會帶上。Access-Control-Allow-Origin
字段,指定爲Origin
字段的值,或者是*
表示接受任意域名的請求。(這個字段是必須的)還可以攜帶其他的字段:比如
Access-Control-Allow-Credentials
(允許攜帶cookie) 、Access-Control-Expose-Headers
(允許XMLHTTPRequest對象的getResponseHeader()拿到響應頭的其他字段的值)。
跨域如何攜帶cookie
如果想要攜帶cookie,必須瀏覽器與服務器同時支持。
-
瀏覽器必須開啓
XMLHTTPRequest
對象的WithCredentials
屬性值爲true
.xhr.withCredentials = true;// 前端設置是否帶cookie
-
服務器響應頭要攜帶
Access-Control-Allow-Credentials
字段的值爲true
.// 允許攜帶cookie res.setHeader('Access-Control-Allow-Credentials', 'true');
注意:如果攜帶了cookie,那麼服務器的Access-Control-Allow-Origin
字段就不能設置爲*
,必須指明的域名。
非簡單請求
預檢請求
如果是非簡單請求,那麼正式的CORS
通信前,會進行一次預檢請求。瀏覽器會先詢問服務器,當前請求的域名是否被服務器所允許,如果服務器允許的話,纔會進行正式的通信。
看一段瀏覽器的腳本文件:
var url = "http://localhost:8084";
var xhr = new XMLHTTPRequest();
xhr.open("PUT",url,true);
// 設置自定義請求頭字段:name
xhr.setRequestHeader('name',"xiaoqi")
xhr.send();
瀏覽器發現這是一個非簡單請求,就會自動發出一個預檢請求,預檢請求的方法是OPTIONS。
針對上面的腳本,對應預檢請求頭信息:
OPTIONS /cors HTTP/1.1
Origin: http://localhost:8081
Access-Control-Request-Method: PUT
Access-Control-Request-Headers:name
Connection:keep-alive
可以看到,除了Origin
字段外,還包括兩個特殊的字段:Access-Control-Request-Method
(該字段必須,用來列出瀏覽器CORS請求會用到的HTTP請求方法)、Access-Control-Request-Headers
(指定瀏覽器CORS請求會額外發送的頭信息字段)
預檢請求迴應
服務器相應的頭信息設置,服務器設置,哪些域名可以訪問,支持的方法,是否可以攜帶cookie等
// 設置哪個源可以訪問我
res.setHeader('Access-Control-Allow-Origin', origin);
// 允許攜帶哪個頭來訪問我
res.setHeader('Access-Control-Allow-Headers', 'name');
// 允許哪個方法訪問我
res.setHeader('Access-Control-Allow-Methods', 'PUT');
// 允許攜帶cookie
res.setHeader('Access-Control-Allow-Credentials', 'true');
// 預檢的存在時間
res.setHeader('Access-Control-Max-Age',6);
// 允許返回的頭
res.setHeader('Access-Control-Expose-Headers', 'name');
當服務器收到預檢請求之後,會檢查Origin
、Access-Control-Request-Method
、Access-Control-Request-Headers
字段後,如果允許,就會做出迴應:
響應頭會帶上的字段有:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:8081
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Headers: name
Access-Control-Allow-Credentials: true
...
如果預檢請求沒有被通過,服務器會返回一個正常的HTTP迴應,但是沒有任何CORS相關的頭信息字段。這時,瀏覽器就會知道服務器不同意這次預檢,返回一個錯誤,被XMLHttpRequest對象的onerror回調函數捕獲 。
正常請求
服務器通過預檢請求,以後每次瀏覽器正常CORS請求,都會和簡單請求一樣,會有一個Origin字段,服務器的迴應也會有Access-Control-Allow-Origin頭信息字段。