1 偶然的相遇——options請求
最近寫的專案,應用裡所有的ajax請求都傳送了2遍。由於新專案,基礎模組是新搭的,所以出現一些奇葩問題也是意料之中,啊終於第一次在chrome的devTools遇見了活的options請求。
1.1 第1次請求
這裡首先傳送了一次額外的options請求,在瀏覽器裡看到請求request header 和 response header的資訊如下:
(1)預檢請求頭request header的關鍵欄位:
Request Header | 作用 |
---|---|
Access-Control-Request-Method | 告訴伺服器實際請求所使用的 HTTP 方法 |
Access-Control-Request-Headers | 告訴伺服器實際請求所攜帶的自定義首部欄位,本次實際請求首部欄位中content-type為自定義 |
伺服器基於從預檢請求頭部獲得的資訊來判斷,是否接受接下來的實際請求。
(2)預檢響應頭response header的關鍵欄位:
response header | 作用 |
---|---|
Access-Control-Allow-Methods | 返回了服務端允許的請求,包含GET/HEAD/PUT/PATCH/POST/DELETE |
Access-Control-Allow-Credentials | 允許跨域攜帶cookie(跨域請求要攜帶cookie必須設定為true) |
Access-Control-Allow-Origin | 允許跨域請求的域名,這個可以在服務端配置一些信任的域名白名單 |
Access-Control-Request-Headers | 客戶端請求所攜帶的自定義首部欄位content-type |
此次OPTIONS請求返回了響應頭的內容,但沒有返回響應實體response body內容。
1.2 第2次請求
這是本來要傳送的請求,如圖所示是普通的post請求。其中Content-Type的application/json是此次和後端約定的請求內容格式,這個也是後面講到為什麼會傳送options請求的原因之一。
2 關於OPTIONS請求
從很多資料我們可以瞭解到使用OPTIONS方法對伺服器發起請求,可以檢測伺服器支援哪些 HTTP 方法。但是這次我們並沒有主動去發起OPTIONS請求,那OPTIONS請求為何會自動發起?
2.1 OPTIONS請求自動發起
MDN的CORS一文中提到:
規範要求,對那些可能對伺服器資料產生副作用的 HTTP 請求方法(特別是 GET 以外的 HTTP 請求,或者搭配某些 MIME 型別的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求。
所以這個跨域請求觸發了瀏覽器自動發起OPTIONS請求,看看此次跨域請求具體觸發了哪些條件。
2.2 跨域請求時,OPTIONS請求觸發條件
CORS預檢請求觸發條件 | 本次請求是否觸發該條件 |
---|---|
1. 使用了下面任一HTTP 方法: | |
PUT/DELETE/CONNECT/OPTIONS/TRACE/PATCH | 否,本次為post請求 |
2. 人為設定了以下集合之外首部欄位: | |
Accept/Accept-Language/Content-Language/Content-Type/DPR/Downlink/Save-Data/Viewport-Width/Width | 否,未設定其他頭部欄位 |
3. Content-Type 的值不屬於下列之一: | |
application/x-www-form-urlencoded、multipart/form-data、text/plain | 是,為application/json |
由於修改了Content-Type為application/json,觸發了CORS預檢請求。
3 優化OPTIONS請求:Access-Control-Max-Age 或者 避免觸發
可見一旦達到觸發條件,跨域請求便會一直髮送2次請求,這樣增加的請求數是否可優化呢?答案是可以,OPTIONS預檢請求的結果可以被快取。
Access-Control-Max-Age這個響應首部表示 preflight request (預檢請求)的返回結果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的資訊) 可以被快取的最長時間,單位是秒。(MDN)
如果值為 -1,則表示禁用快取,每一次請求都需要提供預檢請求,即用OPTIONS請求進行檢測。
評論區的朋友提醒了,儘量避免不要觸發OPTIONS請求,上面例子中把content-type改掉是可以的。在其他場景,比如跨域並且業務有自定義請求頭的話就很難避免了。現在使用的axios或者superagent等第三方ajax外掛,如果出現CORS預檢請求,可以看看預設配置或者二次封裝是否規範。
4 總結
OPTIONS請求即預檢請求,可用於檢測伺服器允許的http方法。當發起跨域請求時,由於安全原因,觸發一定條件時瀏覽器會在正式請求之前自動先發起OPTIONS請求,即CORS預檢請求,伺服器若接受該跨域請求,瀏覽器才繼續發起正式請求。