本人博客文章地址:點擊進入
今天在寫一個簡單的 mock-server 的時候遇到了跨域問題,導致前端頁面不能正常與 mock-server 進行數(shù)據(jù)交互,之后我查詢了相關資料,了解了一下 CORS 的相關知識。
一. 簡介
當一個資源從與該資源本身所在的服務器不同的域或端口請求一個資源時,資源會發(fā)起一個跨域 HTTP 請求。
而CORS 允許瀏覽器向跨源服務器,發(fā)出跨域請求,從而克服了AJAX只能同源使用的限制。
CORS是一個W3C標準,它同時需要瀏覽器和服務端的支持,其中瀏覽器端的支持如下

瀏覽器基本都支持,因此,想要實現(xiàn)CORS通信,只要服務器實現(xiàn)了CORS接口即可。
二. 簡單請求與非簡單請求
cors請求根據(jù)發(fā)出的請求是否滿足一些條件,將請求分為兩種:簡單請求與非簡單請求。
1. 簡單請求
1.1 基本
若請求滿足所有下述條件,則該請求可視為“簡單請求”:
- 請求方法為 Head、Get、Post 中的一個
- 請求頭信息中僅包含 對 CORS 安全的首部字段集合 :Accept、Accept-Language、Content-Language、Content-Type,且 Content-Type 僅限于application/x-www-form-urlencoded、multipart/form-data、 text/plain 中的一個。
對于簡單請求,瀏覽器直接發(fā)出一個CORS請求,并在請求頭中加入一個 Origin 段來描述本次請求來自哪個源(協(xié)議 + 域名 + 端口)。如下就是一個簡單請求的請求頭:
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example
而服務器則需要在返回頭中包含 Access-Control-Allow-Origin 段,來表明哪些外域可以訪問。如下是一個返回頭
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
其中 Access-Control-Allow-Origin:* 表明該資源可以被任意外域訪問。如果服務端僅允許來自 http://foo.example 的訪問,該首部字段的內容如下:Access-Control-Allow-Origin: http://foo.example。現(xiàn)在,除了 http://foo.example,其它外域均不能訪問該資源。
1.2 withCredentials
CORS請求默認不發(fā)送Cookie和HTTP認證信息,如果要發(fā)送Cookie,首先需要在 AJAX 請求中打開 withCredentials 屬性,且服務器的返回頭中必須包含 Access-Control-Allow-Credentials:true 。需要注意的是,當請求中包含cookie時,即打開了 withCredentials 后,服務器返回頭中 Access-Control-Allow-Origin 就不能再設置為 *,而必須要顯示指明允許哪些源訪問。
2. 非簡單請求
2.1 基本
非簡單請求是指那些包含特殊要求的請求,對于這一類請求,瀏覽器會在正式請求之前發(fā)出一個OPTION請求來詢問服務器是否支持該次請求,這個OPTION請求被稱為‘預檢請求’。在服務端回應允許后,瀏覽器才會發(fā)出真正的非簡單請求。
2.2 預檢請求
如下是一個 預檢請求 的請求頭:
OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
除了Origin 字段,還包含了Access-Control-Request-Method 和 Access-Control-Request-Headers 字段,分別表明 CORS 請求會用到的 請求方法 和 額外的請求頭字段。
服務端在收到預檢請求后會做出回應,如下是預檢請求的返回頭:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
Access-Control-Allow-Methods 表示服務端允許的請求方法,Access-Control-Allow-Headers 表示服務端允許的請求頭額外字段,Access-Control-Max-Age 指定本次預檢請求的有效期,有效期內不用發(fā)出另一條預檢請求。
2.2 實際請求
預檢請求完成后,瀏覽器會將實際請求發(fā)出,和簡單請求一致。
三. 相關請求、響應頭字段
1. 請求
Origin:預檢請求或實際請求的源站。協(xié)議 + 域名 + 端口。不管是否為跨域請求,ORIGIN 字段總是會被發(fā)送。
Access-Control-Request-Method:預檢請求專用。將實際請求所使用的 HTTP 方法告訴服務器。
Access-Control-Request-Headers:預檢請求專用。將實際請求所攜帶的額外頭字段告訴服務器。
2. 響應
Access-Control-Allow-Origin:指定允許訪問該資源的外域 URI。當請求打開 withCredentials 屬性時,不可返回 ‘*’。
Access-Control-Expose-Headers:允許瀏覽器通過 getResponseHeader 訪問的額外字段。
Access-Control-Max-Age:預檢請求緩存時間。
Access-Control-Allow-Credentials:當請求打開 withCredentials 屬性時,需要返回true。
Access-Control-Allow-Methods:預檢請求專用。指明實際請求所允許使用的 HTTP 方法。
Access-Control-Allow-Headers:預檢請求專用。指明實際請求所允許攜帶的額外頭字段。
四. 常見跨域錯誤
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'XXX' is therefore not allowed access.
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'XXXX' is therefore not allowed access.
以上兩種錯誤都是由于服務端返回頭沒包含Access-Control-Allow-Origin字段導致的,解決辦法是添加上該字段并讓請求源包含在字段中。
Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains the invalid value 'XXX'. Origin 'XXX' is therefore not allowed access.
The 'Access-Control-Allow-Origin' header contains the invalid value 'XXX'. Origin 'XXX' is therefore not allowed access.
以上兩種錯誤都是由于服務端返回頭中的Access-Control-Allow-Origin字段不包含請求源,解決辦法是修改該字段使請求源包含在字段中。
Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'XXX' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'XXX' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
以上兩種錯誤都是由于請求打開 withCredentials 屬性,但服務端將Access-Control-Allow-Origin設置為了‘’,解決辦法是用精確寫法替換‘’。
Request header field XXX is not allowed by Access-Control-Allow-Headers in preflight response.
這種錯誤是由于請求頭中包含Access-Control-Request-Headers,但服務器未返回對應的Access-Control-Allow-Headers
作者博客地址:https://liuhuihao.com
作者gitHub:https://github.com/geminate