【每日一題NO.82】CORS和預(yù)檢請(qǐng)求


CORS 跨域資源共享
目前幾乎所有公司都是前后端分離的項(xiàng)目,前后端各自的代碼部署在不同的服務(wù)器上,相互通信勢(shì)必會(huì)引發(fā)跨域問題。
老生常談的一個(gè)跨域解決方案就是 CORS (Cross-Origin Resource Sharing)。
使用該方案實(shí)現(xiàn)跨域需要前后端通力合作,需要服務(wù)器選擇性的設(shè)置允許哪些跨域請(qǐng)求訪問服務(wù)器的資源。

關(guān)于CORS的更多了解和設(shè)置可參考:跨域資源共享 CORS 詳解[1]、CORS MDN[2]
后端設(shè)置支持CORS后,前端正常發(fā)送AJAX請(qǐng)求,如果CORS的跨域請(qǐng)求命中非簡(jiǎn)單請(qǐng)求的條件,瀏覽器會(huì)在正式通信前自動(dòng)增加一次查詢請(qǐng)求—— 預(yù)檢請(qǐng)求(preflight)。
知道了預(yù)檢請(qǐng)求和CORS的關(guān)聯(lián)后,我們來詳細(xì)看下什么是預(yù)檢請(qǐng)求、什么情況下會(huì)觸發(fā)預(yù)檢請(qǐng)求。
OPTIONS 方法
先來看下 MDN 中一段關(guān)于 HTTP OPTIONS 方法的描述[3]:
HTTP 的OPTIONS方法,用于獲取目的資源所支持的通信選項(xiàng)。客戶端可以對(duì)特定的 URL 使用OPTIONS方法,也可以對(duì)整站(通過將 URL 設(shè)置為*)使用該方法。
簡(jiǎn)單來說,就是可以用 OPTIONS?請(qǐng)求去嗅探某個(gè)請(qǐng)求在對(duì)應(yīng)的服務(wù)器中都支持哪種請(qǐng)求方法。

預(yù)檢請(qǐng)求
前面說了,在跨域的情況下,瀏覽器發(fā)起復(fù)雜請(qǐng)求前會(huì)主動(dòng)發(fā)起 options 請(qǐng)求。(圖中的第一個(gè)請(qǐng)求)

跨域共享標(biāo)準(zhǔn)規(guī)范要求,對(duì)那些可能對(duì)服務(wù)器數(shù)據(jù)產(chǎn)生副作用的 HTTP 請(qǐng)求方法(特別是 GET 以外的 HTTP 請(qǐng)求,或者搭配某些 MIME 類型的 POST 請(qǐng)求),瀏覽器必須首先使用 options 方法發(fā)起一個(gè)預(yù)檢請(qǐng)求,從而獲知服務(wù)端是否允許該跨域請(qǐng)求。服務(wù)器確認(rèn)允許之后,才發(fā)起實(shí)際的 HTTP 請(qǐng)求。
某些請(qǐng)求不會(huì)觸發(fā) CORS 預(yù)檢請(qǐng)求,這樣的請(qǐng)求一般稱為簡(jiǎn)單請(qǐng)求,而觸發(fā)預(yù)檢請(qǐng)求則稱為復(fù)雜請(qǐng)求。具體命中規(guī)則如下:
簡(jiǎn)單請(qǐng)求
滿足以下條件的請(qǐng)求即為簡(jiǎn)單請(qǐng)求
1、請(qǐng)求方法:
必須為這幾種的其中一個(gè):GET、POST、HEAD
2、請(qǐng)求頭
除了以下請(qǐng)求頭字段之外,沒有自定義的請(qǐng)求頭
AcceptAccept-LanguageContent-LanguageContent-TypeDPRDownlinkSave-DataViewport-WidthWidthContent-Type?得值 僅限于 下列三者之一:text/plain multipart/form-data application/x-www-form-urlencoded
3、關(guān)于XMLHttpRequestUpload
請(qǐng)求中的任意 XMLHttpRequestUpload 對(duì)象均沒有注冊(cè)任何事件監(jiān)聽器;XMLHttpRequestUpload 對(duì)象可以使用 "XMLHttpRequest.upload" 屬性訪問
4、沒有ReadableStream對(duì)象
請(qǐng)求中沒有使用 ReadableStream 對(duì)象
復(fù)雜請(qǐng)求
非簡(jiǎn)單請(qǐng)求 即為復(fù)雜請(qǐng)求。復(fù)雜請(qǐng)求我們也可以稱之為:在實(shí)際進(jìn)行請(qǐng)求之前,需要發(fā)起預(yù)檢請(qǐng)求的請(qǐng)求。
非簡(jiǎn)單請(qǐng)求是那種對(duì)服務(wù)器有特殊要求的請(qǐng)求,比如請(qǐng)求方法是
PUT或DELETE,或者Content-Type字段的類型是"application/json"。
跨域請(qǐng)求時(shí),預(yù)檢請(qǐng)求觸發(fā)的條件
1、請(qǐng)求方法:
使用了下面任意 HTTP 方法的請(qǐng)求:PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH
2、請(qǐng)求頭
人為設(shè)置了以下集合之外的首部字段:
AcceptAccept-LanguageContent-LanguageContent-TypeDPRDownlinkSave-DataViewport-WidthWidthContent-Type的值不屬于下列之一:application/x-www-form-urlencoded multipart/form-data text/plain
截圖一個(gè)預(yù)檢請(qǐng)求的請(qǐng)求頭設(shè)置:

為什么要用預(yù)檢請(qǐng)求
需預(yù)檢的請(qǐng)求要求必須首先使用 OPTIONS 方法發(fā)起一個(gè)預(yù)檢請(qǐng)求到服務(wù)器,以獲知服務(wù)器是否允許該實(shí)際請(qǐng)求。
預(yù)檢請(qǐng)求的使用,可以避免跨域請(qǐng)求對(duì)服務(wù)器的用戶數(shù)據(jù)產(chǎn)生未預(yù)期的影響。
參考資料
阮一峰-跨域資源共享 CORS 詳解: http://www.ruanyifeng.com/blog/2016/04/cors.html
[2]MDN-CORS跨域資源共享: https://developer.mozilla.org/zh-CN/docs/Glossary/CORS
[3]MDN-OPTIONS方法: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/OPTIONS
所有《每日一題》的 知識(shí)大綱索引腦圖 整理在此:https://www.yuque.com/dfe_evernote/interview/everyday
你也可以點(diǎn)擊文末的 “閱讀原文” 快速跳轉(zhuǎn)

