ASP.NET Core 中的回應快取
作者:Rick Anderson 與 Kirk Larkin
檢視或下載範例程式碼 \(英文\) (如何下載)
回應快取可減少用戶端或 Proxy 對 Web 伺服器發出的要求數目。 回應快取也可減少 Web 伺服器執行以產生回應的工作量。 回應快取是在標頭中設定。
ResponseCache 屬性可設定回應快取標頭。 用戶端和中間 Proxy 應遵循「RFC 9111:HTTP 快取」標準中的快取回應標頭。
對於遵循「HTTP 1.1 快取」規範的伺服器端快取,請使用「回應快取中介軟體」。 該中介軟體可使用 ResponseCacheAttribute 屬性來影響伺服器端的快取行為。
回應快取中介軟體:
- 根據 HTTP 快取標頭啟用快取伺服器回應。 實作標準 HTTP 快取語意。 根據 Proxy 之類的 HTTP 快取標頭快取。
- 通常對 Razor Pages 之類的 UI 應用程式並不適用,因為瀏覽器通常會設定造成無法快取的要求標頭。 輸出快取可在 ASP.NET Core 7.0 和更新版本中取得,對 UI 應用程式有益。 使用輸出快取,設定會決定應該獨立於 HTTP 標頭快取的內容。
- 對於來自符合快取條件用戶端的公用 GET 或 HEAD API 要求可能會有幫助。
若要測試回應快取,請使用 Fiddler 或可明確設定要求標頭的其他工具。 明確設定標頭是測試快取的慣用方式。 如需詳細資訊,請參閱疑難排解。
HTTP 型的回應快取
「RFC 9111:HTTP 快取」標準描述了網際網路快取應如何運作。 用於快取的主要 HTTP 標頭是 Cache-Control (用來指定快取指示詞)。 當要求從用戶端傳送到伺服器以及回應從伺服器傳回用戶端時,這些指示詞可控制快取行為。 要求和回應會透過 Proxy 伺服器移動,而 Proxy 伺服器也必須符合「HTTP 1.1 快取」的規範。
下表顯示常見的 Cache-Control
指示詞。
指示詞 | 動作 |
---|---|
public | 快取可以儲存回應。 |
private | 回應不得由共用快取儲存。 私密快取可以儲存並重複使用回應。 |
max-age | 用戶端不接受其時齡大於指定秒數的回應。 範例:max-age=60 (60 秒)、max-age=2592000 (1 個月) |
no-cache | 對於要求:快取不得使用儲存的回應來滿足要求。 源伺服器會重新產生用戶端的回應,而中介軟體會更新其快取中所儲存的回應。 對於回應:若未在原始伺服器上進行驗證,回應不得用於後續的要求。 |
no-store | 對於要求:快取不得儲存要求。 對於回應:快取不得儲存回應的任何部分。 |
下表顯示其他在快取中發揮作用的快取標頭。
標題 | 函式 |
---|---|
Age | 自回應在原始伺服器上產生或成功驗證以來的估計時間 (以秒為單位)。 |
到期 | 回應被視為過時的時間。 |
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 指定了在回應快取中設定適當標頭所需的參數。
警告
請針對包含已驗證用戶端資訊的內容停用快取。 只應針對不會根據使用者的 identity 或使用者是否登入來判斷,是否變更內容來啟用快取。
VaryByQueryKeys 會根據指定查詢索引鍵清單的值來改變儲存的回應。 當提供 *
的單一值時,中介軟體會根據所有要求查詢字串參數來改變回應。
「回應快取中介軟體」必須啟用才能設定 VaryByQueryKeys 屬性。 否則,會擲回執行階段例外狀況。 VaryByQueryKeys 屬性沒有對應的 HTTP 標頭。 此屬性是一個由「回應快取中介軟體」所處理的 HTTP 功能。 為了讓中介軟體提供快取的回應,查詢字串和查詢字串值必須符合先前的要求。 例如,請思慮下表中所示的要求和結果順序:
Request | 傳回自 |
---|---|
http://example.com?key1=value1 |
伺服器 |
http://example.com?key1=value1 |
中介軟體 |
http://example.com?key1=NewValue |
伺服器 |
第一個要求由伺服器傳回並快取在中介軟體中。 第二個要求由中介軟體傳回,因為查詢字串符合先前的要求。 第三個要求不在中介軟體快取中,因為查詢字串值不符合先前的要求。
ResponseCacheAttribute 可用來設定和建立 (透過 IFilterFactory) Microsoft.AspNetCore.Mvc.Internal.ResponseCacheFilter
。 ResponseCacheFilter
會執行更新回應的適當 HTTP 標頭和功能的工作。 該篩選器會:
- 移除
Vary
、Cache-Control
和Pragma
的任何現有標頭。 - 根據 ResponseCacheAttribute 中所設定的屬性寫出適當的標頭。
- 更新回應快取 HTTP 功能 (如果設定了 VaryByQueryKeys)。
變化
只有在設定 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
和 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
。 下列會產生回應標頭,指示用戶端不要儲存回應。
[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 Pages 回應,請將其與 MVC 篩選器或 Razor Pages 篩選器一起新增。
在 MVC 應用程式中:
builder.Services.AddControllersWithViews().AddMvcOptions(options =>
options.Filters.Add(
new ResponseCacheAttribute
{
NoStore = true,
Location = ResponseCacheLocation.None
}));
有關適用於 Razor Pages 應用程式的方法,請參閱將 ResponseCacheAttribute
新增至 MVC 全域篩選器清單不適用於 Razor Pages (dotnet/aspnetcore #18890)。 問題註解中所提供的範例是針對發行 ASP.NET Core 6.0 的 Minimal API 之前版本的應用程式所撰寫的。 對於 6.0 或更新版本的應用程式,請將範例中的服務註冊變更為 Program.cs
的 builder.Services.AddSingleton...
。
位置和持續時間
若要啟用快取,Duration 必須設為正值,而 Location 必須是 Any
(預設值) 或 Client
。 架構會將 Cache-Control
標頭設為回應的位置 (Location) 的值,後面接著 max-age
。
Location 的 Any
和 Client
選項會分別轉換為 public
和 private
的 Cache-Control
標頭值。 如 NoStore 和 Location.None 區段中所述,將 Location 設為 None
會將 Cache-Control
和 Pragma
標頭設為 no-cache
。
Location.Any
(Cache-Control
被設為 public
) 表示用戶端或任何中繼 Proxy 可以快取值,包括「回應快取中介軟體」。
Location.Client
(Cache-Control
被設為 private
) 表示只有用戶端 可以快取值。 任何中繼快取都不應該快取值,包括「回應快取中介軟體」。
快取控制標頭可為用戶端和中繼 Proxy 提供何時以及如何快取回應的指引。 無法保證用戶端和 Proxy 會遵循「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 Pages 時,可以將快取設定檔設定為選項,而不是在許多控制器動作屬性上複製回應快取設定。 在引用的快取設定檔中找到的值將用作 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 Pages:屬性無法套用至處理常式方法中。 與 UI 應用程式一起使用的瀏覽器會阻止快取回應。
- MVC 控制器。
- 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
指示詞。
指示詞 | 動作 |
---|---|
public | 快取可以儲存回應。 |
private | 回應不得由共用快取儲存。 私密快取可以儲存並重複使用回應。 |
max-age | 用戶端不接受其時齡大於指定秒數的回應。 範例:max-age=60 (60 秒)、max-age=2592000 (1 個月) |
no-cache | 對於要求:快取不得使用儲存的回應來滿足要求。 源伺服器會重新產生用戶端的回應,而中介軟體會更新其快取中所儲存的回應。 對於回應:若未在原始伺服器上進行驗證,回應不得用於後續的要求。 |
no-store | 對於要求:快取不得儲存要求。 對於回應:快取不得儲存回應的任何部分。 |
下表顯示其他在快取中發揮作用的快取標頭。
標題 | 函式 |
---|---|
Age | 自回應在原始伺服器上產生或成功驗證以來的估計時間 (以秒為單位)。 |
到期 | 回應被視為過時的時間。 |
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 和 NCache 分散式快取使用。
如需詳細資訊,請參閱 ASP.NET Core 中的分散式快取。
快取標籤協助程式
使用快取標籤協助程式從 MVC 檢視或 Razor Page 快取內容。 快取標籤協助程式會使用記憶體內部快取來儲存資料。
如需詳細資訊,請參閱 ASP.NET Core MVC 中的快取標籤協助程式。
分散式快取標籤協助程式
使用分散式快取標籤協助程式,在分散式雲端或 Web 服務器陣列案例中,從 MVC 檢視或 Razor Page 快取內容。 分散式快取標籤協助程式會使用 SQL Server、Redis 或 NCache 來儲存資料。
如需詳細資訊,請參閱 ASP.NET Core 中的分散式快取標籤協助程式。
ResponseCache 屬性
ResponseCacheAttribute 指定了在回應快取中設定適當標頭所需的參數。
警告
請針對包含已驗證用戶端資訊的內容停用快取。 僅應針對不會根據使用者 identity 或使用者是否登入而變更的內容啟用快取。
VaryByQueryKeys 會根據指定查詢索引鍵清單的值來改變儲存的回應。 當提供 *
的單一值時,中介軟體會根據所有要求查詢字串參數來改變回應。
「回應快取中介軟體」必須啟用才能設定 VaryByQueryKeys 屬性。 否則,會擲回執行階段例外狀況。 VaryByQueryKeys 屬性沒有對應的 HTTP 標頭。 此屬性是一個由「回應快取中介軟體」所處理的 HTTP 功能。 為了讓中介軟體提供快取的回應,查詢字串和查詢字串值必須符合先前的要求。 例如,請思慮下表中所示的要求和結果順序。
Request | 結果 |
---|---|
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)。
變化
只有在設定 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
選項會分別轉換為 public
和 private
的 Cache-Control
標頭值。 如 NoStore 和 Location.None 區段中所述,將 Location 設為 None
會將 Cache-Control
和 Pragma
標頭設為 no-cache
。
Location.Any
(Cache-Control
被設為 public
) 表示用戶端或任何中繼 Proxy 可以快取值,包括「回應快取中介軟體」。
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
快取設定檔
在 Startup.ConfigureServices
中設定 MVC/Razor Pages 時,可以將快取設定檔設定為選項,而不是在許多控制器動作屬性上複製回應快取設定。 在引用的快取設定檔中找到的值將用作 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