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請求,第二次以及第二次以后就只發送真正的請求了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM