CORS跨域時,為何會出現一次動作,兩次請求?

victor318x發表於2019-01-22

提出問題

在開發前後端分離專案時候,我們總會面臨一個跨域問題。

眾所周知,在以前,跨域可以採用代理、JSONP等方式,而在現代瀏覽器面前,我們有了更好的選擇,CORS

我們可以通過伺服器端設定Access-Control-Allow-Origin響應頭,即可使指定來源像訪問同源介面一樣訪問跨域介面。

在使用CORS的時候,後臺採用token檢驗機制,前臺傳送請求必須將token放到Request Header中,那麼就需要傳輸自定義Header資訊,這時候細心的你一定會發現一個問題,在前端ajax請求資料的時候,有時候會向後臺一次性傳送兩次請求,這兩次請求第一次無返回資料,第二次才會返回正確資料。像下圖這個樣子,莫名多出了一個 OPTIONS 的請求:

CORS跨域時,為何會出現一次動作,兩次請求?

CORS跨域時,為何會出現一次動作,兩次請求?

不用懷疑,這不是你的程式碼有bug,也不是在請求函式中重複呼叫了請求,因為很明顯,兩次的 Request Method 是不一樣的。

如果你也曾因這個問題,困惑過,迷茫過,不知所因。那麼關於這個問題,我將為你給出答案!

對於CORS跨域,有兩種不同的請求型別。

  • 簡單跨域請求
  • 複雜跨域請求(帶預檢的跨域請求)。

簡單跨域請求

簡單跨域請求是指滿足以下兩個條件的請求。 1、HTTP方法是以下三種方法之一:

  • HEAD
  • GET
  • POST

2、HTTP的頭資訊不超出以下幾種欄位:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限於三個值,application/x-www-form-urlencoded、multipart/form-data、text/plain

簡單跨域請求的部分響應頭如下:

  • Access-Control-Allow-Origin(必含)- 不可省略,否則請求按失敗處理。該項控制資料的可見範圍,如果希望資料對任何人都可見,可以填寫"*"。

  • Access-Control-Allow-Credentials(可選) – 該項標誌著請求當中是否包含cookies資訊,只有一個可選值:true(必為小寫)。如果不包含cookies,請略去該項,而不是填寫false。這一項與XmlHttpRequest2物件當中的withCredentials屬性應保持一致,即withCredentials為true時該項也為true;withCredentials為false時,省略該項不寫。反之則導致請求失敗。

  • Access-Control-Expose-Headers(可選) – 該項確定XmlHttpRequest2物件當中getResponseHeader()方法所能獲得的額外資訊。通常情況下,getResponseHeader()方法只能獲得如下的資訊:

    • Cache-Control
    • Content-Language
    • Content-Type
    • Expires
    • Last-Modified
    • Pragma

    當你需要訪問額外的資訊時,就需要在這一項當中填寫並以逗號進行分隔。

複雜跨域請求

任何一個不滿足簡單跨域請求要求的請求,即被認為是複雜請求,也稱作帶預檢的跨域請求。

一個複雜請求不止傳送一個包含通訊內容的請求,其中最先傳送的是一種**"預檢"請求**,此時作為服務端,也需要返回**"預回應"**作為響應。"預檢"請求實際上是對服務端的一種許可權請求,只有當"預檢"請求成功返回,實際請求才開始執行。

預請求以OPTIONS形式傳送,當中同樣包含域,並且還包含了兩項CORS特有的內容:

  • Access-Control-Request-Method – 該項內容是實際請求的種類,可以是GET、POST之類的簡單請求,也可以是PUT、DELETE等等。
  • Access-Control-Request-Headers – 該項是一個以逗號分隔的列表,當中是複雜請求所使用的頭部。

顯而易見,這個"預檢"請求實際上就是在為之後的實際請求傳送一個許可權請求,在預回應返回的內容當中,服務端應當對這兩項進行回覆,以讓瀏覽器確定請求是否能夠成功完成。一旦預回應如期而至,所請求的許可權也都已滿足,才會發出真實請求,攜帶真實資料。

解決方法

現在問題所在已經很明顯了,那麼面對這種跨域預檢機制造成的多次請求問題,我們可以在後臺設定Access-Control-Max-Age來控制瀏覽器在多長時間內(單位s)無需在請求時傳送預檢請求,從而減少不必要的預檢請求。

關於CORS的更細緻的問題可以檢視MDN

相關文章