ASP.NET Core 中的回應快取中介軟體

作者:John LuoRick Anderson

本文說明如何在 ASP.NET Core 應用程式中設定回應快取中介軟體。 中介軟體會判斷回應何時可供快取、儲存回應,以及提供來自快取的回應。 如需 HTTP 快取和 [ResponseCache] 屬性的簡介,請參閱回應快取

回應快取中介軟體:

  • 根據 HTTP 快取標頭啟用快取伺服器回應。 實作標準 HTTP 快取語意。 根據 Proxy 之類的 HTTP 快取標頭快取。
  • 通常對 Razor Pages 之類的 UI 應用程式並不適用,因為瀏覽器通常會設定造成無法快取的要求標頭。 輸出快取可在 ASP.NET Core 7.0 和更新版本中取得,對 UI 應用程式有益。 使用輸出快取,設定會決定應該獨立於 HTTP 標頭快取的內容。
  • 對於來自符合快取條件用戶端的公用 GET 或 HEAD API 要求可能會有幫助。

若要測試回應快取,請使用 Fiddler 或其他可明確設定要求標頭的工具。 明確設定標頭是測試快取的慣用方式。 如需詳細資訊,請參閱疑難排解

組態

Program.cs 中,將回應快取中介軟體服務 AddResponseCaching 新增至服務集合,並將應用程式設定為搭配 UseResponseCaching 擴充方法使用中介軟體。 UseResponseCaching 將中介軟體新增至要求處理管線:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCaching();

var app = builder.Build();

app.UseHttpsRedirection();

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

app.UseResponseCaching();

警告

在使用 CORS 中介軟體之前,必須在 UseResponseCaching 之前先呼叫 UseCors

範例應用程式會新增標頭來控制後續要求的快取:

  • 快取控制:快取最多 10 秒的可快取回應。
  • 不同:只有當後續要求的 Accept-Encoding 標頭符合原始要求時,才設定中介軟體來提供快取的回應。
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCaching();

var app = builder.Build();

app.UseHttpsRedirection();

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

app.UseResponseCaching();

app.Use(async (context, next) =>
{
    context.Response.GetTypedHeaders().CacheControl =
        new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
        {
            Public = true,
            MaxAge = TimeSpan.FromSeconds(10)
        };
    context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] =
        new string[] { "Accept-Encoding" };

    await next();
});

app.MapGet("/", () => DateTime.Now.Millisecond);

app.Run();

上述標頭不會寫入回應,而且會在控制器、動作或 Razor Page 時被覆寫:

  • 具有 [ResponseCache] 屬性。 即使未設定屬性,也是如此。 例如,省略 VaryByHeader 屬性會導致從回應中移除對應的標頭。

回應快取中介軟體只會快取導致 200 (OK) 狀態碼的伺服器回應。 中介軟體會忽略任何其他回應,包括錯誤頁面

警告

包含已驗證用戶端內容的回應必須標示為不可快取,以防止中介軟體儲存及處理這些回應。 如需中介軟體如何判斷回應是否可快取的詳細資訊,請參閱 快取的條件

上述程式碼通常不會將快取的值傳回至瀏覽器。 使用 FiddlerPostman 或其他工具,可明確設定要求標頭,並偏好用於測試快取。 如需詳細資訊,請參閱本文中的疑難排解

選項。

下表顯示回應快取選項。

選項 描述
MaximumBodySize 回應主體的最大可快取大小,以位元組為單位。 預設值為 64 * 1024 * 1024 (64 MB)。
SizeLimit 回應快取中介軟體的大小限制,以位元組為單位。 預設值為 100 * 1024 * 1024 (100 MB)。
UseCaseSensitivePaths 判斷回應是否在區分大小寫的路徑上快取。 預設值是 false

下列範例會將中介軟體設定為:

  • 快取主體大小小於或等於 1,024 個位元組的回應。
  • 依區分大小寫的路徑儲存回應。 例如,/page1/Page1 會分開儲存。
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCaching(options =>
{
    options.MaximumBodySize = 1024;
    options.UseCaseSensitivePaths = true;
});

var app = builder.Build();

app.UseHttpsRedirection();

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

app.UseResponseCaching();

app.Use(async (context, next) =>
{
    context.Response.GetTypedHeaders().CacheControl =
        new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
        {
            Public = true,
            MaxAge = TimeSpan.FromSeconds(10)
        };
    context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] =
        new string[] { "Accept-Encoding" };

    await next(context);
});

app.MapGet("/", () => DateTime.Now.Millisecond);

app.Run();

VaryByQueryKeys

使用 MVC、Web API 控制器或 Razor Pages 頁面模型時,[ResponseCache] 屬性指定為回應快取設定適當標頭所需的參數。 [ResponseCache] 屬性中唯一嚴格要求中介軟體的參數是 VaryByQueryKeys,它與實際的 HTTP 標頭不對應。 如需詳細資訊,請參閱 ASP.NET Core 中的回應快取

在不使用 [ResponseCache] 屬性時,回應快取可能會隨著 VaryByQueryKeys 而有所不同。 直接從 HttpCoNtext.Features 使用 ResponseCachingFeature

var responseCachingFeature = context.HttpContext.Features.Get<IResponseCachingFeature>();

if (responseCachingFeature != null)
{
    responseCachingFeature.VaryByQueryKeys = new[] { "MyKey" };
}

VaryByQueryKeys 中使用等於 * 的單一值會根據所有請求查詢參數而改變快取。

回應快取中介軟體所使用的 HTTP 標頭

下表提供影響回應快取的 HTTP 標頭相關資訊。

標題 詳細資料
Authorization 如果標頭存在,則不會快取回應。
Cache-Control 中介軟體只會考慮針對標有 public 快取指示詞的回應進行快取。 使用下列參數控制快取:
  • 存留期上限
  • max-stale†
  • min-fresh
  • must-revalidate
  • 無快取
  • 無存放區
  • only-if-cached
  • private
  • public
  • s-maxage
  • proxy-revalidate‡
†如果未將限制指定為 max-stale,中介軟體就不會採取任何動作。
proxy-revalidate 的效果與 must-revalidate 相同。

如需詳細資訊,請參閱 RFC 9111:要求指示詞
Pragma 要求中的 Pragma: no-cache 標頭會產生與 Cache-Control: no-cache 相同的效果。 該標頭將被 Cache-Control 標頭中的相關指示詞 (如果存在) 覆寫。 考慮與 HTTP/1.0 的回溯相容性。
Set-Cookie 如果標頭存在,則不會快取回應。 在要求處理管線中設定一或多個 cookie 的中介軟體,會防止回應快取中介軟體進行快取回應 (例如 cookie 型 TempData 提供者)。
Vary 標頭 Vary 會用來變更另一個標頭的快取回應。 例如,透過包含 Vary: Accept-Encoding 標頭進行編碼來快取回應,它分別快取具有標頭 Accept-Encoding: gzipAccept-Encoding: text/plain 的要求的回應。 具有 * 標頭值的回應永遠不會儲存。
Expires 除非被其他 Cache-Control 標頭覆寫,否則不會儲存或擷取此標頭視為過時的回應。
If-None-Match 如果值不是 *,且回應的 ETag 不符合提供的任何值,則會從快取提供完整回應。 否則,會提供 304 (未修改) 的回應。
If-Modified-Since 如果 If-None-Match 標頭不存在,且快取的回應日期比提供的值新,則將從快取提供完整回應。 否則,會提供 304 - 未修改的回應。
Date 當從快取提供服務時,如果原始回應中未提供 Date 標頭,則由中介軟體設定 Date 標頭。
Content-Length 當從快取提供服務時,如果原始回應中未提供 Content-Length 標頭,則由中介軟體設定 Content-Length 標頭。
Age 原始回應中傳送的 Age 標頭會遭忽略。 中介軟體會在提供快取回應時計算新的值。

快取會遵守要求 Cache-Control 指示詞

中介軟體遵守 RFC 9111:HTTP 快取規則 (第 5.2 節。快取控制)。 規則需要快取,才能接受用戶端所傳送的有效 Cache-Control 標頭。 在規格下,用戶端可以使用標頭值 no-cache 提出要求,並強制伺服器為每個要求產生新的回應。 目前,使用中介軟體時,沒有任何開發人員控制此快取行為,因為中介軟體遵守官方快取規格。

若要進一步控制快取行為,請探索 ASP.NET Core 的其他快取功能。 請參閱下列主題的說明:

疑難排解

回應快取中介軟體會使用容量有限的 IMemoryCache。 當超出容量時,記憶體快取將被壓縮

如果快取行為不如預期,請確認回應可進行快取且能夠從快取提供。 檢查要求的傳入標頭和回應的傳出標頭。 啟用記錄以協助偵錯。

測試快取行為並進行疑難排解時,瀏覽器通常會設定防止快取的要求標頭。 例如,瀏覽器可能會在重新整理頁面時,將 Cache-Control 標頭設定為 no-cachemax-age=0FiddlerPostman 和其他工具可以明確設定要求標頭,並偏好用於測試快取。

快取的條件

  • 要求必須產生具有 200 (OK) 狀態碼的伺服器回應。
  • 要求方法必須是 GET 或 HEAD。
  • 回應快取中介軟體必須放在需要快取的中介軟體之前。 如需詳細資訊,請參閱 ASP.NET Core 中介軟體
  • 標頭 Authorization 不得存在。
  • Cache-Control 標頭參數必須為有效,而且回應必須標示 public 且未標示 private
  • 如果 Cache-Control 標頭不存在,則 Pragma: no-cache 標頭不得存在,因為 Cache-Control 標頭會覆寫存在的 Pragma 標頭。
  • 標頭 Set-Cookie 不得存在。
  • Vary 標頭參數必須有效且不等於 *
  • Content-Length 標頭值 (如有設定) 必須符合回應主體的大小。
  • IHttpSendFileFeature 未使用。
  • 回應不得過時,如 Expires 標頭和 max-ages-maxage 快取指示詞所指定。
  • 回應緩衝必須成功。 回應的大小必須小於設定或預設 SizeLimit。 回應的主體大小必須小於所設定或預設 MaximumBodySize
  • 根據 RFC 9111:HTTP 快取,回應必須可快取。 例如,no-store 指示詞不能存在於要求或回應標頭欄位中。 如需詳細資訊,請參閱 RFC 9111:HTTP 快取 (第 3 節:將回應儲存在快取中

注意

用於產生安全權杖的 Antiforgery 系統可防止跨網站偽造要求 (CSRF) 攻擊將 Cache-ControlPragma 標頭設定為 no-cache,以便回應不會被快取。 如需如何停用 HTML 表單元素的反偽造權杖資訊,請參閱在 ASP.NET Core 中防止跨網站偽造要求 (XSRF/CSRF) 攻擊

其他資源

本文說明如何在 ASP.NET Core 應用程式中設定回應快取中介軟體。 中介軟體會判斷回應何時可供快取、儲存回應,以及提供來自快取的回應。 如需 HTTP 快取和 [ResponseCache] 屬性的簡介,請參閱回應快取

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

組態

回應快取中介軟體可透過共用架構隱含提供給 ASP.NET Core 應用程式。

Startup.ConfigureServices 中,將回應快取中介軟體新增至服務集合:

public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCaching();
    services.AddRazorPages();
}

將應用程式設定為搭配 UseResponseCaching 擴充方法使用中介軟體,將中介軟體新增至 Startup.Configure 中的要求處理管線:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
    }

    app.UseStaticFiles();
    app.UseRouting();
    // UseCors must be called before UseResponseCaching
    // app.UseCors("myAllowSpecificOrigins");

    app.UseResponseCaching();

    app.Use(async (context, next) =>
    {
        context.Response.GetTypedHeaders().CacheControl = 
            new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
            {
                Public = true,
                MaxAge = TimeSpan.FromSeconds(10)
            };
        context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] = 
            new string[] { "Accept-Encoding" };

        await next();
    });

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

警告

在使用 CORS 中介軟體之前,必須在 UseResponseCaching 之前先呼叫 UseCors

範例應用程式會新增標頭來控制後續要求的快取:

  • 快取控制:快取最多 10 秒的可快取回應。
  • 不同:只有當後續要求的 Accept-Encoding 標頭符合原始要求時,才設定中介軟體來提供快取的回應。
// using Microsoft.AspNetCore.Http;

app.Use(async (context, next) =>
{
    context.Response.GetTypedHeaders().CacheControl = 
        new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
        {
            Public = true,
            MaxAge = TimeSpan.FromSeconds(10)
        };
    context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] = 
        new string[] { "Accept-Encoding" };

    await next();
});

上述標頭不會寫入回應,而且會在控制器、動作或 Razor Page 時被覆寫:

  • 具有 [ResponseCache] 屬性。 即使未設定屬性,也是如此。 例如,省略 VaryByHeader 屬性會導致從回應中移除對應的標頭。

回應快取中介軟體只會快取導致 200 (OK) 狀態碼的伺服器回應。 中介軟體會忽略任何其他回應,包括錯誤頁面

警告

包含已驗證用戶端內容的回應必須標示為不可快取,以防止中介軟體儲存及處理這些回應。 如需中介軟體如何判斷回應是否可快取的詳細資訊,請參閱 快取的條件

選項。

下表顯示回應快取選項。

選項 描述
MaximumBodySize 回應主體的最大可快取大小,以位元組為單位。 預設值為 64 * 1024 * 1024 (64 MB)。
SizeLimit 回應快取中介軟體的大小限制,以位元組為單位。 預設值為 100 * 1024 * 1024 (100 MB)。
UseCaseSensitivePaths 判斷回應是否在區分大小寫的路徑上快取。 預設值是 false

下列範例會將中介軟體設定為:

  • 快取主體大小小於或等於 1,024 個位元組的回應。
  • 依區分大小寫的路徑儲存回應。 例如,/page1/Page1 會分開儲存。
services.AddResponseCaching(options =>
{
    options.MaximumBodySize = 1024;
    options.UseCaseSensitivePaths = true;
});

VaryByQueryKeys

使用 MVC/Web API 控制器或 Razor Pages 頁面模型時,[ResponseCache] 屬性會指定為設定回應快取適當標頭所需的參數。 [ResponseCache] 屬性中唯一嚴格要求中介軟體的參數是 VaryByQueryKeys,它與實際的 HTTP 標頭不對應。 如需詳細資訊,請參閱 ASP.NET Core 中的回應快取

在不使用 [ResponseCache] 屬性時,回應快取可能會隨著 VaryByQueryKeys 而有所不同。 直接從 HttpCoNtext.Features 使用 ResponseCachingFeature

var responseCachingFeature = context.HttpContext.Features.Get<IResponseCachingFeature>();

if (responseCachingFeature != null)
{
    responseCachingFeature.VaryByQueryKeys = new[] { "MyKey" };
}

VaryByQueryKeys 中使用等於 * 的單一值會根據所有請求查詢參數而改變快取。

回應快取中介軟體所使用的 HTTP 標頭

下表提供影響回應快取的 HTTP 標頭相關資訊。

標題 詳細資料
Authorization 如果標頭存在,則不會快取回應。
Cache-Control 中介軟體只會考慮針對標有 public 快取指示詞的回應進行快取。 使用下列參數控制快取:
  • 存留期上限
  • max-stale†
  • min-fresh
  • must-revalidate
  • 無快取
  • 無存放區
  • only-if-cached
  • private
  • public
  • s-maxage
  • proxy-revalidate‡
†如果未將限制指定為 max-stale,中介軟體就不會採取任何動作。
proxy-revalidate 的效果與 must-revalidate 相同。

如需詳細資訊,請參閱 RFC 9111:要求指示詞
Pragma 要求中的 Pragma: no-cache 標頭會產生與 Cache-Control: no-cache 相同的效果。 該標頭將被 Cache-Control 標頭中的相關指示詞 (如果存在) 覆寫。 考慮與 HTTP/1.0 的回溯相容性。
Set-Cookie 如果標頭存在,則不會快取回應。 在要求處理管線中設定一或多個 cookie 的中介軟體,會防止回應快取中介軟體進行快取回應 (例如 cookie 型 TempData 提供者)。
Vary 標頭 Vary 會用來變更另一個標頭的快取回應。 例如,透過包含 Vary: Accept-Encoding 標頭進行編碼來快取回應,它分別快取具有標頭 Accept-Encoding: gzipAccept-Encoding: text/plain 的要求的回應。 具有 * 標頭值的回應永遠不會儲存。
Expires 除非被其他 Cache-Control 標頭覆寫,否則不會儲存或擷取此標頭視為過時的回應。
If-None-Match 如果值不是 *,且回應的 ETag 不符合提供的任何值,則會從快取提供完整回應。 否則,會提供 304 (未修改) 的回應。
If-Modified-Since 如果 If-None-Match 標頭不存在,且快取的回應日期比提供的值新,則將從快取提供完整回應。 否則,會提供 304 - 未修改的回應。
Date 當從快取提供服務時,如果原始回應中未提供 Date 標頭,則由中介軟體設定 Date 標頭。
Content-Length 當從快取提供服務時,如果原始回應中未提供 Content-Length 標頭,則由中介軟體設定 Content-Length 標頭。
Age 原始回應中傳送的 Age 標頭會遭忽略。 中介軟體會在提供快取回應時計算新的值。

快取會遵守要求 Cache-Control 指示詞

中介軟體遵守 RFC 9111:HTTP 快取規則 (第 5.2 節。快取控制)。 規則需要快取,才能接受用戶端所傳送的有效 Cache-Control 標頭。 在規格下,用戶端可以使用標頭值 no-cache 提出要求,並強制伺服器為每個要求產生新的回應。 目前,使用中介軟體時,沒有任何開發人員控制此快取行為,因為中介軟體遵守官方快取規格。

若要進一步控制快取行為,請探索 ASP.NET Core 的其他快取功能。 請參閱下列主題的說明:

疑難排解

如果快取行為不如預期,請確認回應可進行快取且能夠從快取提供。 檢查要求的傳入標頭和回應的傳出標頭。 啟用記錄以協助偵錯。

測試快取行為並進行疑難排解時,瀏覽器可能會設定要求標頭,並以不想要的方式影響快取。 例如,瀏覽器可能會在重新整理頁面時,將 Cache-Control 標頭設定為 no-cachemax-age=0。 下列工具可以明確設定要求標頭,並偏好用於測試快取:

快取的條件

  • 要求必須產生具有 200 (OK) 狀態碼的伺服器回應。
  • 要求方法必須是 GET 或 HEAD。
  • Startup.Configure 中,回應快取中介軟體必須放在需要快取的中介軟體之前。 如需詳細資訊,請參閱 ASP.NET Core 中介軟體
  • 標頭 Authorization 不得存在。
  • Cache-Control 標頭參數必須為有效,而且回應必須標示 public 且未標示 private
  • 如果 Cache-Control 標頭不存在,則 Pragma: no-cache 標頭不得存在,因為 Cache-Control 標頭會覆寫存在的 Pragma 標頭。
  • 標頭 Set-Cookie 不得存在。
  • Vary 標頭參數必須有效且不等於 *
  • Content-Length 標頭值 (如有設定) 必須符合回應主體的大小。
  • IHttpSendFileFeature 未使用。
  • 回應不得過時,如 Expires 標頭和 max-ages-maxage 快取指示詞所指定。
  • 回應緩衝必須成功。 回應的大小必須小於設定或預設 SizeLimit。 回應的主體大小必須小於所設定或預設 MaximumBodySize
  • 根據 RFC 9111:HTTP 快取,回應必須可快取。 例如,no-store 指示詞不能存在於要求或回應標頭欄位中。 如需詳細資訊,請參閱 RFC 9111:HTTP 快取 (第 3 節:將回應儲存在快取中

注意

用於產生安全權杖的 Antiforgery 系統可防止跨網站偽造要求 (CSRF) 攻擊將 Cache-ControlPragma 標頭設定為 no-cache,以便回應不會被快取。 如需如何停用 HTML 表單元素的反偽造權杖資訊,請參閱在 ASP.NET Core 中防止跨網站偽造要求 (XSRF/CSRF) 攻擊

其他資源