1. 程式人生 > 程式設計 >ASP.NET Core中的Http快取使用

ASP.NET Core中的Http快取使用

Http響應快取可減少客戶端或代理對web伺服器發出的請求數。響應快取還減少了web伺服器生成響應所需的工作量。響應快取由Http請求中的header控制。

ASP.NET Core對其都有相應的實現,並不需要了解裡面的工作細節,即可對其進行良好的控制。

瞭解Http快取

Http協議中定義了許多快取,但總體可以分為強快取和協商快取兩類。

ASP.NET Core中的Http快取使用

強快取

強快取是指快取命中時,客戶端不會向伺服器發請求,瀏覽器F12能看到響應狀態碼為200sizefrom cache,它的實現有以下幾種方式:

Expires - 絕對時間

示例:Expires:Thu,31 Dec 2037 23:59:59 GMT

,就表示快取有效期至2037年12月31日,在這之前瀏覽器都不會向伺服器發請求了(除非按F5/Ctrl+F5重新整理)。

Cache-Control - 相對時間/更多控制

絕對時間是一個絕對時間,因為計算時不方便;而且服務端是依據伺服器的時間來返回,但客戶端卻需要依據客戶的時間來判斷,因此也容易失去控制。

Cache-Control有以下選項(可以多選):

  1. max-age: 指定一個時間長度,在這個時間段內快取是有效的,單位是秒(s)。例如設定Cache-Control:max-age=31536000,也就是說快取有效期為31536000/24/60/60=365天。
  2. s-maxage: 同max-age,覆蓋max-age、Expires,但僅適用於共享快取,在私有快取中被忽略。
  3. public: 表明響應可以被任何物件(傳送請求的客戶端、代理伺服器等等)快取。
  4. private: 表明響應只能被單個使用者(可能是作業系統使用者、瀏覽器使用者)快取,是非共享的,不能被代理伺服器快取。
  5. no-cache: 強制所有快取了該響應的使用者,在使用已快取的資料前,傳送帶驗證器的請求到伺服器。(不是字面意思上的不快取)
  6. no-store: 禁止快取,每次請求都要向伺服器重新獲取資料。
  7. must-revalidate: 指定如果頁面是過期的,則去伺服器進行獲取。(意思是瀏覽器在某些情況下,快取失效後仍可使用老快取,加了這個頭,失效後就必須驗證,並不是字面上有沒有過期都驗證)

其中最有意思的要數no-cachemust-revalidate了,因為它們的表現都不是字面意義。

no-cache並不是字面上的不快取,而是會一直服務端驗證(真實意義很像字面上的must-revalidate)。

must-revalidate是隻是為了給瀏覽器強調,快取過期後,千萬要遵守約定重新驗證。

協商快取

協商快取是指快取命中時,伺服器返回Http狀態碼為304但無內容(Body),沒命中時返回200有內容。

在要精細控制時,協商快取比強快取更有用,它有Last-ModifiedETag兩種。

Last-Modified/If-Modify-Since(對比修改時間)

示例:

伺服器:Last-Modified: Sat,27 Jun 2015 16:48:38 GMT
客戶端:If-Modified-Since: Sat,27 Jun 2015 16:48:38 GMT

ETag/If-None-Match(對比校驗碼)

伺服器:ETag: W/"0a0b8e05663d11:0"
客戶端:If-None-Match: W/"0a0b8e05663d11:0"

清快取要點

  1. F5重新整理時,強快取失效
  2. Ctrl+F5重新整理時 強快取和協商快取都失效

ASP.NET Core的Http快取

ASP.NET Core中提供了ResponseCacheAttribute來實現快取,它的定義如下:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,AllowMultiple = false,Inherited = true)]
public class ResponseCacheAttribute : Attribute,IFilterFactory,IFilterMetadata,IOrderedFilter
{
  public ResponseCacheAttribute();
  public string CacheProfileName { get; set; }
  public int Duration { get; set; }
  public bool IsReusable { get; }
  public ResponseCacheLocation Location { get; set; }
  public bool NoStore { get; set; }

  public int Order { get; set; }
  public string VaryByHeader { get; set; }
  public string[] VaryByQueryKeys { get; set; }
}

其中,ResponseCacheLocation定義了快取的位置,是重點:

//   Determines the value for the "Cache-control" header in the response.
public enum ResponseCacheLocation
{
  //   Cached in both proxies and client. Sets "Cache-control" header to "public".
  Any = 0,//   Cached only in the client. Sets "Cache-control" header to "private".
  Client = 1,//   "Cache-control" and "Pragma" headers are set to "no-cache".
  None = 2
}

注意看原始檔中的註釋,Any表示Cache-Control: publicClient表示Cache-Control: privateNone表示Cache-Control: no-cache

注意ResponseCacheLocation並沒有定義將快取放到伺服器的選項。

其中Duration表示快取時間,單位為秒,它將翻譯為max-age

另外可以通過VaryByHeaderVaryByQueryKeys來配置快取要不要通過headerquery string來變化,其中VaryByHeader是通過Http協議中的Vary頭來實現的,VaryByQueryKeys必須通過Middleware來實現。

不要誤會,所有ResponseCacheAttribute的屬性配置都不會在服務端快取你的響應資料(雖然你可能有這種錯覺),它和輸出快取不同,它沒有狀態,只用來做客戶端強快取。

如果不想快取,則設定NoStore = true,它會設定cache-control: no-store,我們知道no-store的真實意思是不快取。一般還會同時設定Location = ResponseCacheLocation.None,它會設定cache-control: no-cache(真實意思是表示一定會驗證)。

注意單獨設定Location = ResponseCacheLocation.None而不設定NoStore並不會有任何效果。

示例1

這是一個很典型的使用示例:

public class HomeController : Controller
{
  [ResponseCache(Duration = 3600,Location = ResponseCacheLocation.Client)]
  public IActionResult Data()
  {
    return Json(DateTime.Now);
  }
}

我定義了3600秒的快取,並且cache-control應該為private,生成的Http快取頭可以通過如下C#程式碼來驗證:

using var http = new HttpClient();
var resp1 = await http.GetAsync("https://localhost:55555/home/data");
Console.WriteLine(resp1.Headers.CacheControl.ToString());
Console.WriteLine(await resp1.Content.ReadAsStringAsync());

輸入結果如下:

max-age=3600,private
"2020-03-07T21:35:01.5843686+08:00"

另外,ResponseCacheAttribute也可以定義在Controller級別上,表示整個Controller都受到快取的影響。

CacheProfileName示例

另外,如果需要共用快取配置,可以使用CacheProfileName,將快取提前定義好,之後直接傳入這個定義名即可使用:

.ConfigureServices(s =>
{
  s.AddControllers(o =>
  {
    o.CacheProfiles.Add("3500",new CacheProfile
    {
      Duration = 3500,Location = ResponseCacheLocation.Client,});
  });
});

這樣我就定義了一個名為3500的快取,稍後在Controller中我只需傳入CacheProfileName = 3500即可:

public class HomeController : Controller
{
  [ResponseCache(CacheProfileName = "3500")]
  public IActionResult Data()
  {
    return Json(DateTime.Now);
  }
}

總結

Http快取分為強快取和協商快取,ASP.NET Core提供了便利的ResponseCacheAttribute實現了強快取,還能通過Profile來批量配置多個快取點。

ASP.NET MVC並沒有提供協商快取實現,因為這些多半和業務邏輯相關,需要自己寫程式碼。靜態檔案是特例,Microsoft.AspNetCore.StaticFiles中提供有,因為靜態檔案的邏輯很清晰。

ASP.NET中的OutputCacheAttributeASP.NET Core中不復存在,取而代之的是app/services.AddResponseCaching(),這些和Http協議不相關。

有機會我會具體聊聊這些快取。

到此這篇關於ASP.NET Core中的Http快取使用的文章就介紹到這了,更多相關ASP.NET Core Http快取內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!