跨域資源共享(Cross-Origin Resource Sharing)是一種機制,它使用額外的 HTTP 頭部告訴瀏覽器可讓一個web應用進行跨域資源請求。前端
若一個請求同時知足下述全部條件,則該請求可視爲「簡單請求」<span style="color:grey">(注:灰色字體內容瞭解便可)</span>:ios
使用的方法爲git
GET
HEAD
POST
手動設置的頭部字段只能是(注意:也能夠設置 Forbidden header name 中的頭部字段,如 Connection
、Accept-Encoding
等,可是設置無效)github
Accept
Accept-Language
Content-Language
Content-Type
(值的範圍還要符合下面的要求)DPR
</div>Downlink
</div>Save-Data
</div>Viewpoer-Width
</div>Width
</div>Content-Type
的值只能爲web
application/x-www-form-urlencoded
multipart/form-data
text/plain
XMLHttpRequestUpload
object used in the request; these are accessed using the XMLHttpRequest.upload property.</div>CORS 預檢請求發生在實際請求以前,用於檢查服務器是否支持 CORS,以判斷實際請求發送是否安全。預檢請求使用的方式是 OPTIONS
。axios
當一個請求不是「簡單請求」時,即應該先發送預檢請求,好比:後端
GET
、HEAD
、POST
X-xxx
Content-Type
值不是 application/x-www-form-urlencoded
、 multipart/form-data
、text/plain
,等等跨域請求,CORS要求服務端設置一些頭部字段,最重要的一個就是 Access-Control-Allow-Origin
。下面以案例進行說明,前端使用 axios 進行 http 傳輸,後端以 koa 做爲服務端框架,並使用CORS中間件 koa2-cors。api
// Client http://localhost:8080 simpleRequest() { axios({ method: 'GET', url: 'http://localhost:3000/api/simple' }).then(data => { console.log(data); }); }
// Server http://localhost:3000 app.use(cors()); router.get('/api/simple', ctx => { ctx.body = { result: 'simple request success' }; });
HTTP 報文:跨域
HTTP 請求頭部有個 Origin
字段,表示請求來自哪裏。HTTP 響應頭部中的 Access-Control-Allow-Origin
表示哪一個域能夠訪問該資源。使用 Origin
和 Access-Control-Allow-Origin
就完成了最簡單的訪問控制。瀏覽器
// Client http://localhost:8080 mainRequest() { axios({ method: 'POST', url: 'http://localhost:3000/api/mainRequest', headers: { 'X-test': 'CORS' } // 增長一個自定義的頭部字段,觸發預檢請求 }).then(data => { console.log(data); }); }
// Server http://localhost:3000 app.use(cors()); router.post('/api/mainRequest', ctx => { ctx.body = { result: 'main request success' }; });
預檢請求的報文:
請求首部字段 Access-Control-Request-Method
告知服務器,實際請求將使用 POST
方法。
請求首部字段 Access-Control-Request-Headers
告知服務器,實際請求將攜帶一個自定義請求首部字段:x-test。服務器據此決定,該實際請求是否被容許。
響應首部字段 Access-Control-Allow-Methods
代表服務器容許客戶端使用哪些方法發起請求。
響應首部字段 Access-Control-Allow-Headers
代表服務器容許請求中攜帶字段 x-test。
實際請求的報文:
實際請求中發送了 X-test
頭部字段,響應狀態碼 200 OK。
能夠看到,預檢請求中 Client 和 Server 使用了更多的頭部字段來完成訪問控制。那麼,CORS 相關的請求頭部字段和響應頭部字段共有哪些呢?
Origin
頭部字段表示預檢請求或實際請求的源站。Access-Control-Request-Method
頭部字段用於預檢請求。其做用是,將實際請求所使用的 HTTP 方法告訴服務器。Access-Control-Request-Headers
頭部字段用於預檢請求。其做用是,將實際請求所攜帶的首部字段告訴服務器。注意,以上請求頭部字段無須手動設置,當使用 XMLHttpRequest
對象發起跨域請求時,它們已經被設置就緒。
Access-Control-Allow-Origin
其語法以下:
Access-Control-Allow-Origin: <origin> | *
origin 參數的值指定了容許訪問該資源的外域 URI。若是該字段的值爲通配符 *
,則表示容許來自全部域的請求。
注意,若是服務端指定了具體的域名而非 *
,那麼響應頭部中的 Vary
字段的值必須包含 Origin
。這將告訴客戶端:服務器對不一樣的源站返回不一樣的內容。
Access-Control-Allow-Methods
頭部字段用於預檢請求的響應。其指明瞭實際請求所容許使用的 HTTP 方法。Access-Control-Allow-Headers
頭部字段用於預檢請求的響應。其指明瞭實際請求中容許攜帶的首部字段。Access-Control-Expose-Headers
跨域請求中,瀏覽器默認狀況下經過API只能獲取到如下響應頭部字段:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
若是想要訪問其餘響應頭部信息,則須要在服務器端設置 Access-Control-Allow-Headers
。Access-Control-Expose-Headers
讓服務器把容許瀏覽器訪問的頭部字段放入白名單,好比:
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
這樣瀏覽器就可以訪問到 X-My-Custom-Header
和 X-Another-Custom-Header
響應頭部了。
Access-Control-Max-Age Access-Control-Max-Age
字段指定了預檢請求的結果可以被緩存多久,單位是 秒,好比:
Access-Control-Max-Age: 5
表示在第一次預檢請求發出後,5s 內再訪問該接口時會直接發送實際請求,而不須要先發預檢請求。過了 5s 後,會再要求先發送預檢請求,以此類推。
app.use( cors({ maxAge: 5 }) );
服務端設置了 5s 緩存,實際請求以下:
注意,若是設置緩存後,發現每次仍是會發送 OPTIONS 請求,請檢查你是否是勾選了「禁止緩存」。
Access-Control-Allow-Credentials XMLHttpRequest.withCredentials
(或者 Request.credentials
)表示跨域請求中,user agent 是否應該發送 cookies、authorization headers 或者 TLS client certificates 等憑據。Access-Control-Allow-Credentials
的做用就是:當 credentials 爲 「真」 時(XHR和Fetch設置方式不同),Access-Control-Allow-Credentials
告訴瀏覽器是否把響應內容暴露給前端 JS 代碼。好比:
// Client http://localhost:8080 simpleRequest() { axios({ method: 'GET', url: 'http://localhost:3000/api/simple', withCredentials: true // 增長了withCredentials 選項 }).then(data => { console.log(data); }); } // Server http://localhost:3000 app.use( cors({ maxAge: 5, // credentials: true }) );
此時,服務端未設置 credentials: true
,發起請求能看到客戶端報錯:
若是服務端設置了 credentials: true
則客戶端就不會報錯了。
預檢請求的時候,Access-Control-Allow-Credentials
響應頭部字段表示實際請求中是否能夠使用 credentials。
關於 CORS 響應頭部字段的運用,建議看一下 koa2-cors 中間件的源碼。代碼只有幾十行,特別清晰易懂。
CORS 相關內容如上,瞭解以後能更好地幫助咱們解決平常聯調中出現的問題,好比:出現跨域了服務端怎麼設置,axios.post
方法發送一個對象時爲何會出現 OPTIONS 請求,代理服務器怎麼才能轉發cookies等等。