1. 程式人生 > 實用技巧 >HTTP OPTIONS跨域請求

HTTP OPTIONS跨域請求

一、場景

今天在監測跨域程式碼時發現,在呼叫後端介面的時候會出現兩次請求:OPTIONS請求和POST請求。程式碼如下:

/// <summary>
/// 自定義中介軟體要執行的邏輯
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
  context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
  context.Response.Headers.Add(
"Access-Control-Allow-Headers", "*");   context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");   //若為OPTIONS跨域請求則直接返回,不進入後續管道   if (context.Request.Method.ToUpper() != "OPTIONS")     await _next(context);//把context傳進去執行下一個中介軟體 }

二、原因

XMLHttpRequest會遵守同源策略(same-origin policy),即指令碼只能訪問相同協議/相同主機名/相同埠的資源。 突破這個限制的情況叫做跨域,此時需要遵守跨域資源共享標準CORS(Cross-Origin Resource Sharing)機制。瀏覽器將CORS請求分為兩類:簡單跨域請求(simple request)和非簡單跨域請求(not-simple-request)。簡單請求瀏覽器請求不會觸發預檢請求,而非簡單請求會觸發預檢請求。

三、跨域請求

跨域請求分為簡單跨域請求和非簡單跨域請求,這兩種方式是怎麼區分的呢?

1、簡單跨域請求

(1)同時滿足下列以下條件,就屬於簡單請求,否則屬於非簡單請求

  • 請求方式只能是:GET、POST、HEAD
  • HTTP請求頭限制以下幾種欄位(不得人為設定該集合之外的其他首部欄位):
Accept、Accept-Language、Content-Language、Content-Type(需要注意額外的限制)、DPR、Downlink、Save-Data、Viewport-Width、Width
  • Content-type只能取:application/x-www-form-urlencoded、multipart/form-data、text/plain
  • 請求中的任意XMLHttpRequestUpload 物件均沒有註冊任何事件監聽器;XMLHttpRequestUpload 物件可以使用 XMLHttpRequest.upload 屬性訪問。
  • 請求中沒有使用 ReadableStream 物件。

(2)簡單請求瀏覽器請求不會觸發預檢請求

2、非簡單跨域請求

非簡單請求會在正式通訊之前,增加一次HTTP請求,稱之為預檢請求。瀏覽器會先發起OPTIONS方法到伺服器,以獲知伺服器是否允許該實際請求。

四、如何避免非簡單跨域請求

我們通過上邊已經知道,只要滿足簡單請求的條件就可以避免發起OPTIONS請求,但是在實際開發中,簡單請求肯定不會滿足需求,比如以下請求:

  • 我們系統請求中除了GET/POST還有PUT,DELETE
  • 系統做了許可權認證,請求頭裡需要帶有使用者驗證資訊
  • 我們的Content-Type絕大多數是application/json

然後只能寄希望於減少發起OPTIONS請求的次數,也就是說還是會用,但不是每次都用,方法命令如下:

Access-Control-Max-Age:(number) 數值代表預檢請求的返回結果 可以被快取多久,單位是秒

例如將預檢請求的結果快取10分鐘:

/// <summary>
/// 自定義中介軟體要執行的邏輯
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
  context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
   context.Response.Headers.Add("Access-Control-Allow-Headers", "*");
   context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
   context.Response.Headers.Add("Access-Control-Max-Age", "600");
   //若為OPTIONS跨域請求則直接返回,不進入後續管道
   if (context.Request.Method.ToUpper() != "OPTIONS")
       await _next(context);//把context傳進去執行下一個中介軟體
}

注意:

  • 不同瀏覽器有不同的上限,在Firefox中上限是24h,而在Chromium中則是10min,Chromium同時規定了一個預設值 5 秒。如果值為-1,則表示禁用快取,每一次請求都需要提供預檢請求。
  • Access-Control-Max-Age方法對完全一樣的url的快取設定生效,多一個引數也視為不同url。也就是說,如果設定了10分鐘的快取,在10分鐘內,所有請求第一次會產生options請求,第二次以及第二次以後就只發送真正的請求了。