ASP.NET Core 中的回應快取

作者:Rick Anderson 與 Kirk Larkin

回應快取可減少用戶端或 Proxy 對 Web 伺服器發出的要求數目。 回應快取也可減少 Web 伺服器執行以產生回應的工作量。 回應快取是在標頭中設定。

ResponseCache 屬性可設定回應快取標頭。 用戶端及中介代理應尊重 RFC 9111:HTTP 快取 規範下的快取回應標頭。

對於遵循 HTTP 1.1 快取規範的伺服器端快取,請使用 響應快取中介軟體。 該中介軟體可使用 ResponseCacheAttribute 屬性來影響伺服器端的快取行為。

回應快取中介軟體允許基於 HTTP Cache-Control 標頭對伺服器回應進行快取。

  • 快取行為實作標準的 HTTP 快取語意。

  • 快取是基於類似代理伺服器使用的方法的 HTTP 快取標頭。

  • 這種快取形式對於滿足 快取條件 的客戶端公開的 GET 或 HEAD API 請求非常有用。

  • 對於像 Pages 這類 Razor UI 應用程式,回應快取通常沒什麼幫助。 瀏覽器通常會設定請求標頭以防止快取。

    Output caching(.NET 7 及以後版本提供)是 UI 應用中更好的方法。 在這種情況下,設定會決定要快取哪些內容,獨立於 HTTP 標頭之外。

要測試回應快取,可以使用 Fiddler 或其他能明確設定請求標頭的工具。 明確設定標頭是測試快取的慣用方式。 欲了解更多資訊,請參閱 回應快取中介軟體 > 故障排除。

檢視或下載範例程式碼 \(英文\) (如何下載)

基於 HTTP 的回應快取

RFC 9111:HTTP 快取規範描述了網際網路快取應如何運作。 用於快取的主要 HTTP 標頭是 Cache-Control (用來指定快取指示詞)。 指令控制快取行為,當請求從客戶端傳送到伺服器,回應從伺服器返回客戶端時。 要求和回應會透過 Proxy 伺服器移動,而 Proxy 伺服器也必須符合「HTTP 1.1 快取」的規範。

下表顯示常見的 Cache-Control 指示詞。

Directive Action
public 快取可以儲存回應。
private 共享快取不得儲存回應。 私有快取可以儲存並重複使用回應。
max-age 用戶端不接受其時齡大於指定秒數的回應。 範例: max-age=60 (60秒)、 max-age=2592000 (一個月)
no-cache 對於要求:快取不得使用儲存的回應來滿足要求。 源伺服器會重新產生用戶端的回應,而中介軟體會更新其快取中所儲存的回應。

對於回應:若未在原始伺服器上進行驗證,回應不得用於後續的要求。
no-store 針對請求:快取不得儲存該請求。

對於回應:快取不得儲存回應的任何部分。

下表顯示其他在快取中發揮作用的快取標頭。

Header Function
Age 提供自回應在原始伺服器產生或成功驗證以來,所花費的時間(秒數)估計。
Expires 指定在何時之後,回應會被視為過時。
Pragma 存在是為了向下相容 HTTP 1.0 快取,用於設定 no-cache 行為。 如果存在 Cache-Control 標頭,則會忽略 Pragma 標頭。
Vary 指定除非快取回應的原始要求和新要求中的所有 Vary 標頭欄位都相符,否則不得傳送快取的回應。

HTTP 型的快取遵從請求的 Cache-Control 指令

RFC 9111:HTTP 快取(第 5.2 節 Cache-Control)規範要求快取必須尊重客戶端傳送的有效Cache-Control標頭。 用戶端可以使用 no-cache 標頭值來發出要求,並強制伺服器為每個要求產生新的回應。

如果您考慮 HTTP 快取的目的,則始終遵循用戶端 Cache-Control 要求標頭是有意義的。 根據官方的規範,快取的目的是為了降低滿足網路上用戶端、Proxy 和伺服器要求時的延遲和網路負荷。 它不一定是控制原始伺服器上的負載的方法。

使用 響應快取中介 軟體時,開發者無法控制這種快取行為,因為中介軟體遵循官方快取規範。 .NET 7 及以後的版本支援輸出快取,以更好地控制伺服器負載。 如需詳細資訊,請參閱輸出快取。

ResponseCache 屬性

該 ResponseCacheAttribute 類別指定在回應快取中設定適當標頭所需的參數。

Warning

請針對包含已驗證用戶端資訊的內容停用快取。 只有針對不會因為使用者的身分識別或是否登入而變更的內容才應啟用快取。

此 VaryByQueryKeys 特性會將儲存的回應隨所給查詢鍵列表的值變化。 當提供通配符星號(*)的單一值時,中介軟體會根據所有請求查詢字串參數來調整回應。

必須啟用回應快取中介軟體,才能設定 VaryByQueryKeys 屬性。 否則,會拋出執行階段例外狀況。 VaryByQueryKeys 屬性沒有對應的 HTTP 標頭。 此屬性是由回應快取中介軟體處理的 HTTP 功能。 為了讓中介軟體提供快取的回應,查詢字串和查詢字串值必須符合先前的請求。 例如,請思慮下表中所示的要求和結果順序。

Request 傳回自
http://example.com?key1=value1 Server
http://example.com?key1=value1 Middleware
http://example.com?key1=NewValue Server

伺服器回傳第一個請求,中介軟體則快取該請求。 中介軟體回傳第二個請求,因為查詢字串與前一個(第一個)請求相符。 第三個要求不在中介軟體快取中,因為查詢字串值不符合先前的要求。

ResponseCacheAttribute 類別用於配置並建立(透過 IFilterFactory 介面)Microsoft.AspNetCore.Mvc.Internal.ResponseCacheFilter。 ResponseCacheFilter 會執行更新回應的適當 HTTP 標頭和功能的工作。 篩選:

  • 移除 Vary、Cache-Control 和 Pragma 的任何現有標頭。
  • 根據 ResponseCacheAttribute 中所設定的屬性寫出適當的標頭。
  • 如果 VaryByQueryKeys 設定了該屬性,則更新回應快取 HTTP 功能。

Vary 標頭

只有在設定 VaryByHeader 屬性時才會寫入此標頭。 該屬性會被設定為Vary屬性的值。 下列範例使用了 VaryByHeader 屬性:

[ApiController]
public class TimeController : ControllerBase
{
    [Route("api/[controller]")]
    [HttpGet]
    [ResponseCache(VaryByHeader = "User-Agent", Duration = 30)]
    public ContentResult GetTime() => Content(
                      DateTime.Now.Millisecond.ToString());

請使用 Fiddler 或其他工具來檢視回應標頭。 回應標頭包括:

Cache-Control: public,max-age=30
Vary: User-Agent

前述程式碼需要將回應快取中介軟體 AddResponseCaching 服務方法加入服務集合中。 程式碼用擴充功能方法設定應用程式使用中介軟體 UseResponseCaching 。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddResponseCaching();

var app = builder.Build();

app.UseHttpsRedirection();

// UseCors must be called before UseResponseCaching
//app.UseCors();

app.UseResponseCaching();

app.UseAuthorization();

app.MapControllers();

app.Run();

無商店與地點。無物業

該 NoStore 屬性覆蓋大多數其他屬性。 當此屬性設為 true 時,Cache-Control 標頭會設定為 no-store。 若屬性 Location 設定為 None:

  • Cache-Control標頭設為 no-store,no-cache。
  • Pragma標頭設為 no-cache。

若 NoStore 屬性設為 false ,且 Location 屬性設為 None,則 Cache-Control 和 Pragma 標頭設為 no-cache。

NoStore這個屬性通常會設定為true錯誤頁面。 以下程式碼產生回應標頭,指示用戶端不要儲存回應。

[Route("api/[controller]/ticks")]
[HttpGet]
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public ContentResult GetTimeTicks() => Content(
                  DateTime.Now.Ticks.ToString());

上面的程式碼會在回應中包含下列標頭:

Cache-Control: no-store,no-cache
Pragma: no-cache

若要將 ResponseCacheAttribute 套用至應用程式的所有 MVC 控制器或 Razor 頁面回應,請新增屬性,並使用 MVC 過濾器 或 Razor 頁面過濾器。

在 MVC 應用程式中,請新增以下程式碼:

builder.Services.AddControllersWithViews().AddMvcOptions(options => 
    options.Filters.Add(
        new ResponseCacheAttribute
        {
            NoStore = true, 
            Location = ResponseCacheLocation.None
        }));

關於適用於 Razor 頁面應用程式的方法,請參見 GitHub dotnet/aspnetcore issue #18890) - 將 ResponseCacheAttribute 加入 MVC 全域過濾器清單不適用於 Razor Pages。 本期評論範例適用於 ASP.NET Core於 Minimal APIS(ASP.NET Core 6.0 版本)發布之前的應用程式。 針對 6.0 及以後版本的應用程式,請將範例中使用的服務註冊改為 builder.Services.AddSingleton...Program.cs 檔案中的服務註冊。

位置與持續時間特性

要啟用快取, Duration 屬性必須設定為正值,且 Location 必須是 Any (預設值)或 Client。 架構會將 Cache-Control 標頭設為回應的位置 (Location) 的值,後面接著 max-age。

Location和 Any 的Client屬性選項分別轉換為 Cache-Control 和 public的private標頭值。 如NoStore 和 Location.None 章節所述,設定Location屬性為None時,會將Cache-Control 和Pragma 標頭都設為no-cache。

Location.Any(Cache-Control 設為 public)屬性設定表示,用戶端或任何中繼 Proxy都可以快取此值,包括回應快取中介軟體。

屬性設定Location.Client(Cache-Control 設定為 private)表示只有客戶端能快取該值。 不應有任何中介快取快取該值,包括 回應快取中介軟體。

快取控制標頭為客戶端與中介代理提供何時以及如何快取回應的指引。 無法保證用戶端和代理會遵守 RFC 9111:HTTP 快取 規範。 回應快取中介軟體 始終遵循規範中所訂定的快取規則。

以下範例展示了設定 Duration 屬性並保留預設 Location 屬性值所產生的標頭:

[Route("api/[controller]/ms")]
[HttpGet]
[ResponseCache(Duration = 10, Location = ResponseCacheLocation.Any, NoStore = false)]
public ContentResult GetTimeMS() => Content(
                  DateTime.Now.Millisecond.ToString());

上面的程式碼會在回應中包含下列標頭:

Cache-Control: public,max-age=10

快取配置檔

與其重複許多控制器動作屬性的回應快取設定,可以在設定 MVC 頁面或 Razor 頁面時設定快取設定檔作為選項。 在參考快取設定檔中找到的值會被用作預設值。ResponseCacheAttribute 在屬性上指定的任何屬性值都會覆寫參考的快取設定檔值。

以下的範例展示了一個 30 秒的快取配置:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCaching();
builder.Services.AddControllers(options =>
{
    options.CacheProfiles.Add("Default30",
        new CacheProfile()
        {
            Duration = 30
        });
});

var app = builder.Build();

app.UseHttpsRedirection();

// UseCors must be called before UseResponseCaching
//app.UseCors();

app.UseResponseCaching();

app.UseAuthorization();

app.MapControllers();

app.Run();

下列程式碼引用了該 Default30 的快取設定檔:

[ApiController]
[ResponseCache(CacheProfileName = "Default30")]
public class Time2Controller : ControllerBase
{
    [Route("api/[controller]")]
    [HttpGet]
    public ContentResult GetTime() => Content(
                      DateTime.Now.Millisecond.ToString());

    [Route("api/[controller]/ticks")]
    [HttpGet]
    public ContentResult GetTimeTicks() => Content(
                      DateTime.Now.Ticks.ToString());
}

該 Default30 的快取設定檔所產生的標頭回應包括:

Cache-Control: public,max-age=30

你可以將 [ResponseCache] 屬性套用到 Razor 頁面、MVC 控制器和 MVC 動作方法。

在 Razor Pages 中,你無法對處理方法套用屬性,因為搭配 UI 應用程式的瀏覽器會阻止回應快取。 在 MVC 動作方法中,方法層級的屬性會覆蓋類別層級屬性中指定的設定。

下列程式碼會在控制器層級和方法層級套用 [ResponseCache] 屬性:

[ApiController]
[ResponseCache(VaryByHeader = "User-Agent", Duration = 30)]
public class Time4Controller : ControllerBase
{
    [Route("api/[controller]")]
    [HttpGet]
    public ContentResult GetTime() => Content(
                      DateTime.Now.Millisecond.ToString());

    [Route("api/[controller]/ticks")]
    [HttpGet]
    public ContentResult GetTimeTicks() => Content(
                  DateTime.Now.Ticks.ToString());

    [Route("api/[controller]/ms")]
    [HttpGet]
    [ResponseCache(Duration = 10, Location = ResponseCacheLocation.Any, NoStore = false)]
    public ContentResult GetTimeMS() => Content(
                      DateTime.Now.Millisecond.ToString());
}

檢視或下載範例程式碼 \(英文\) (如何下載)

回應快取可減少用戶端或 Proxy 對 Web 伺服器發出的要求數目。 回應快取也可減少 Web 伺服器執行以產生回應的工作量。 回應快取是由標頭 (其指定您希望用戶端、Proxy 和中介軟體如何快取回應) 所控制。

[ResponseCache] 參與設定回應快取標頭。 用戶端和中間 Proxy 應遵循「RFC 9111:HTTP 快取」標準中的快取回應標頭。

對於遵循 HTTP 1.1 快取規範的伺服器端快取,請使用 響應快取中介軟體。 該中介軟體可使用 [ResponseCache] 屬性來設定伺服器端快取標頭。

基於 HTTP 的回應快取

「RFC 9111:HTTP 快取」標準描述了網際網路快取應如何運作。 用於快取的主要 HTTP 標頭是 Cache-Control (用來指定快取指示詞)。 當請求從用戶端傳送到伺服器以及回應從伺服器傳送回用戶端時,這些指令可控制快取行為。 要求和回應會透過 Proxy 伺服器移動,而 Proxy 伺服器也必須符合「HTTP 1.1 快取」的規範。

下表顯示常見的 Cache-Control 指示詞。

Directive Action
public 快取可以儲存回應。
private 回應不得由共用快取儲存。 私密快取可以儲存並重複使用回應。
max-age 用戶端不接受其時齡大於指定秒數的回應。 範例:max-age=60 (60 秒)、max-age=2592000 (1 個月)
no-cache 對於要求:快取不得使用儲存的回應來滿足要求。 源伺服器會重新產生用戶端的回應,而中介軟體會更新其快取中所儲存的回應。

對於回應:若未在原始伺服器上進行驗證,回應不得用於後續的要求。
no-store 針對請求:快取不得儲存該請求。

對於回應:快取不得儲存回應的任何部分。

下表顯示其他在快取中發揮作用的快取標頭。

Header Function
Age 自回應在原始伺服器上產生或成功驗證以來的估計時間 (以秒為單位)。
Expires 回應被視為過時的時間。
Pragma 存在是為了與 HTTP/1.0 快取向後相容,以設定無 no-cache 行為。 如果存在 Cache-Control 標頭,則會忽略 Pragma 標頭。
Vary 指定除非快取回應的原始要求和新要求中的所有 Vary 標頭欄位都相符,否則不得傳送快取的回應。

HTTP 型的快取遵從請求的 Cache-Control 指令

RFC 9111:HTTP 快取 (第 5.2 節Cache-Control) 要求快取遵循用戶端所傳送的有效 Cache-Control 標頭。 用戶端可以使用 no-cache 標頭值來發出要求,並強制伺服器為每個要求產生新的回應。

如果您考慮 HTTP 快取的目的,則始終遵循用戶端 Cache-Control 要求標頭是有意義的。 根據官方的規範,快取的目的是為了降低滿足網路上用戶端、Proxy 和伺服器要求時的延遲和網路負荷。 它不一定是控制原始伺服器上的負載的方法。

使用 響應快取中介 軟體時,開發者無法控制這種快取行為,因為中介軟體遵循官方快取規範。 對輸出快取 的支援以更妥善地控制伺服器負載,是 ASP.NET Core 未來版本的一個設計提案。 如需詳細資訊,請參閱新增對輸出快取的支援 (dotnet/aspnetcore #27387)。

ASP.NET Core 中的其他快取技術

記憶體緩存

記憶體內部快取會使用伺服器記憶體來儲存快取的資料。 這種類型的快取適合使用工作階段相關性的單一伺服器或多伺服器。 工作階段親和性也稱為黏性工作階段。 工作階段黏著性表示來自用戶端的請求會路由至相同伺服器進行處理。

如需詳細資訊,請參閱 ASP.NET Core 中的記憶體內部快取和疑難排解 Azure 應用程式閘道工作階段親和性問題。

分佈式快取

當應用程式裝載於雲端或伺服器陣列時,請使用分散式快取將資料儲存在記憶體中。 快取會由處理請求的伺服器共享。 如果用戶端的快取資料可供使用,用戶端可以提交要求供群組中的任何伺服器處理。 ASP.NET Core 適用於 SQL Server、 Redis、 Postgres 和 NCache 分散式快取。

如需詳細資訊,請參閱 ASP.NET Core 中的分散式快取。

快取標籤協助程式

使用 Cache Tag Helper 在 MVC 檢視或 Razor Page 快取內容。 快取標籤協助器使用記憶體內部的快取來儲存資料。

如需詳細資訊,請參閱 ASP.NET Core MVC 中的快取標籤協助程式。

分散式快取標籤協助程式

使用分散式快取標籤協助程式,在分散式雲端或 Web 伺服器陣列場景中,快取 MVC 檢視或 Razor 頁面的內容。 分散式快取標籤協助程式會使用 SQL Server、Redis 或 NCache 來儲存資料。

如需詳細資訊,請參閱 ASP.NET Core 中的分散式快取標籤協助程式。

ResponseCache 屬性

ResponseCacheAttribute 指定了在回應快取中設定適當標頭所需的參數。

Warning

請針對包含已驗證用戶端資訊的內容停用快取。 只有針對不會因為使用者的身分識別或是否登入而變更的內容才應啟用快取。

VaryByQueryKeys 會根據指定查詢索引鍵清單的值來改變儲存的回應。 當提供 * 的單一值時,中介軟體會根據所有要求查詢字串參數來改變回應。

必須啟用回應快取中介軟體,才能設定 VaryByQueryKeys 屬性。 否則,會拋出執行階段例外狀況。 VaryByQueryKeys 屬性沒有對應的 HTTP 標頭。 此屬性是由回應快取中介軟體處理的 HTTP 功能。 為了讓中介軟體提供快取的回應,查詢字串和查詢字串值必須符合先前的請求。 例如,請思慮下表中所示的要求和結果順序。

Request Result
http://example.com?key1=value1 從伺服器傳回。
http://example.com?key1=value1 從中介軟體傳回。
http://example.com?key1=value2 從伺服器傳回。

第一個請求由伺服器回應並緩存在中介軟體中。 第二個要求由中介軟體傳回,因為查詢字串符合先前的要求。 第三個要求不在中介軟體快取中,因為查詢字串值不符合先前的要求。

ResponseCacheAttribute 可用來設定和建立 (透過 IFilterFactory) Microsoft.AspNetCore.Mvc.Internal.ResponseCacheFilter。 ResponseCacheFilter 會執行更新回應的適當 HTTP 標頭和功能的工作。 篩選:

  • 移除 Vary、Cache-Control 和 Pragma 的任何現有標頭。
  • 根據 ResponseCacheAttribute 中所設定的屬性寫出適當的標頭。
  • 更新回應快取 HTTP 功能 (如果設定了 VaryByQueryKeys)。

Vary

只有在設定 VaryByHeader 屬性時才會寫入此標頭。 該屬性被設定為 Vary 屬性值。 下列範例使用了 VaryByHeader 屬性:

[ResponseCache(VaryByHeader = "User-Agent", Duration = 30)]
public class Cache1Model : PageModel
{

使用範例應用程式,並透過瀏覽器的網路工具來檢視回應標頭。 下列回應標頭會隨 Cache1 頁面回應一起傳送:

Cache-Control: public,max-age=30
Vary: User-Agent

NoStore 和 Location.None

NoStore 會覆寫大部分的其他屬性。 當此屬性設為 true 時,Cache-Control 標頭會設定為 no-store。 如果 Location 設為 None:

  • Cache-Control 設定為 no-store,no-cache。
  • Pragma 設定為 no-cache。

如果 NoStore 為 false 且 Location 為 None,則 Cache-Control 和 Pragma 會設定為 no-cache。

對於錯誤頁面,NoStore 通常會設定為 true。 範例應用程式中的 Cache2 頁面會產生回應標頭,指示用戶端不要儲存回應。

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class Cache2Model : PageModel
{

範例應用程式會傳回具有下列標頭的 Cache2 頁面:

Cache-Control: no-store,no-cache
Pragma: no-cache

若要將 ResponseCacheAttribute 套用至應用程式的所有 MVC 控制器或 Razor Pages 回應,請將其與 MVC 篩選器或 Razor Pages 篩選器一起新增。

在 MVC 應用程式中:

services.AddMvc().AddMvcOptions(options => 
    options.Filters.Add(
        new ResponseCacheAttribute
        {
            NoStore = true, 
            Location = ResponseCacheLocation.None
        }));

有關適用於 Razor Pages 應用程式的方法,請參閱將 ResponseCacheAttribute 新增至 MVC 全域篩選器清單不適用於 Razor Pages (dotnet/aspnetcore #18890)。

位置和持續時間

若要啟用快取,Duration 必須設為正值,而 Location 必須是 Any (預設值) 或 Client。 架構會將 Cache-Control 標頭設為回應的位置 (Location) 的值,後面接著 max-age。

Location 的 Any 和 Client 選項會分別轉換為 Cache-Control 和 public 的 private 標頭值。 如 NoStore 和 Location.None 區段中所述,將 Location 設為 None 會將 Cache-Control 和 Pragma 標頭設為 no-cache。

Location.Any (Cache-Control 設為 public) 表示 用戶端或任何中介代理 可以快取該值,包括 響應快取中介軟體。

Location.Client (Cache-Control 被設為 private) 表示只有用戶端 可以快取值。 不應有任何中介快取快取該值,包括 回應快取中介軟體。

快取控制標頭僅為用戶端和中繼 Proxy 提供何時以及如何快取回應的指引。 無法保證用戶端和 Proxy 會遵循「RFC 9111:HTTP 快取」標準。 回應快取中介軟體 始終遵循規範中所訂定的快取規則。

下列範例顯示了範例應用程式中的 Cache3 頁面模型,以及透過設定 Duration 並保留預設 Location 值所產生的標頭:

[ResponseCache(Duration = 10, Location = ResponseCacheLocation.Any, NoStore = false)]
public class Cache3Model : PageModel
{

範例應用程式傳回了具有下列標頭的 Cache3 頁面:

Cache-Control: public,max-age=10

快取配置檔

設定 MVC/Razor Pages 時,可以在 Startup.ConfigureServices 中將快取設定檔配置為選項,而不是在許多控制器動作屬性上複製響應快取設定。 在引用的快取設定檔中找到的值將用作 ResponseCacheAttribute 的預設值,並且會被該屬性上指定的任何屬性值所覆寫。

設定快取設定檔。 下列範例在範例應用程式的 Startup.ConfigureServices 中顯示了一個 30 秒的快取設定檔:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddMvc(options =>
    {
        options.CacheProfiles.Add("Default30",
            new CacheProfile()
            {
                Duration = 30
            });
    });
}

範例應用程式的 Cache4 頁面模型引用了 Default30 快取設定檔:

[ResponseCache(CacheProfileName = "Default30")]
public class Cache4Model : PageModel
{

ResponseCacheAttribute 可以套用至:

  • Razor Pages:屬性無法套用至處理常式方法中。
  • MVC 控制器。
  • MVC 動作方法:方法層級屬性會覆寫類別層級屬性中所指定的設定。

該 Default30 的快取設定檔所產生的標頭回應 (套用至 Cache4 頁面):

Cache-Control: public,max-age=30

其他資源