ASP.NET Core 中的响应缓存
作者:Rick Anderson 和 Kirk Larkin
响应缓存可减少客户端或代理对 Web 服务器发出的请求数。 响应缓存还减少了 Web 服务器为生成响应而执行的工作量。 响应缓存在标头中设置。
ResponseCache 属性可设置响应缓存标头。 客户端和中间代理应遵循RFC 9111:HTTP 缓存下缓存响应的标头。
对于遵循 HTTP 1.1 缓存规范的服务器端缓存,请使用响应缓存中间件。 中间件可以使用 ResponseCacheAttribute 属性来影响服务器端缓存行为。
响应缓存中间件:
- 启用基于 HTTP 缓存头的缓存服务器响应。 实现标准 HTTP 缓存语义。 像代理一样基于 HTTP 缓存标头进行缓存。
- 通过对 Razor Pages 等 UI 应用没有好处,因为浏览器通常会设置阻止缓存的请求头。 ASP.NET Core 7.0 及更高版本中提供的输出缓存将有利于 UI 应用。 使用输出缓存,配置可决定了应独立于 HTTP 标头缓存的内容。
- 对于来自满足缓存条件的客户端的公共 GET 或 HEAD API 请求可能有用。
若要测试响应缓存,请使用 Fiddler 或可显式设置请求头的其他工具。 显式设置标头是测试缓存的首选项。 有关详细信息,请参阅疑难解答。
基于 HTTP 的响应缓存
RFC 9111:HTTP 缓存介绍了 Internet 缓存的行为方式。 用于缓存的主 HTTP 标头是 Cache-Control,它用于指定缓存指令。 当请求从客户端到达服务器以及响应从服务器返回客户端时,这些指令控制缓存行为。 请求和响应在代理服务器之间移动,并且代理服务器还必须符合 HTTP 1.1 缓存规范。
下表中显示了常用 Cache-Control
指令。
指令 | 操作 |
---|---|
公共 | 缓存可以存储响应。 |
private | 响应不得由共享缓存存储。 专用缓存可以存储和重用响应。 |
max-age | 客户端不接受期限大于指定秒数的响应。 示例:max-age=60 (60 秒),max-age=2592000 (1 个月) |
no-cache | 请求时:缓存不能使用存储的响应来满足请求。 源服务器重新生成客户端的响应,中间件更新其缓存中存储的响应。 响应时:响应不得用于未经源服务器验证的后续请求。 |
no-store | 请求时:缓存不得存储请求。 响应时:缓存不得存储任何部分的响应。 |
下表显示了在缓存中发挥了作用的其他缓存标头。
Header | 函数 |
---|---|
帐龄 | 在源服务器上生成或成功验证响应以来的估计时间量(以秒为单位)。 |
到期 | 响应被视为过时后的时间。 |
Pragma | 为与用于设置 no-cache 行为的 HTTP/1.0 缓存向后兼容而存在。 如果 Cache-Control 标头存在,则将忽略 Pragma 标头。 |
Vary | 指定除非所有 Vary 标头字段在缓存响应的原始请求和新请求中都匹配,否则不得发送缓存响应。 |
基于 HTTP 的缓存遵循请求 Cache-Control 指令
RFC 9111:HTTP 缓存(第 5.2 节. Cache-Control)要求缓存遵循客户端发送的有效Cache-Control
标头。 客户端可以使用 no-cache
标头值发出请求,并强制服务器针对每个请求生成新的响应。
如果考虑 HTTP 缓存的目标,则始终遵循客户端 Cache-Control
请求标头是有意义的。 根据官方规范,缓存旨在减少在客户端、代理和服务器网络中满足请求的延迟和网络开销。 它不一定是控制源服务器上的负载的一种方法。
使用响应缓存中间件时,开发人员无法控制此缓存行为,因为该中间件遵循官方缓存规范。 .NET 7 中添加了对输出缓存的支持,从而更好地控制服务器负载。 有关详细信息,请参阅输出缓存。
ResponseCache 属性
ResponseCacheAttribute 指定在响应缓存中设置相应标头所需的参数。
警告
对包含经过身份验证的客户端信息的内容禁用缓存。 仅应为不会根据用户标识或用户是否登录而更改的内容启用缓存。
VaryByQueryKeys 根据给定的查询键列表的值改变存储的响应。 当提供了 *
的单个值时,中间件将根据所有请求查询字符串参数改变响应。
必须启用响应缓存中间件才能设置 VaryByQueryKeys 属性。 否则,会引发运行时异常。 VaryByQueryKeys 属性没有对应的 HTTP 标头。 属性是一个 HTTP 功能,由响应缓存中间件进行处理。 对于用于缓存响应的中间件,查询字符串和查询字符串值必须与上一个请求匹配。 例如,请考虑下表中所示的请求和结果的顺序:
请求 | 返回自 |
---|---|
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 中设置的属性写出相应标头。
- 如果设置了 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
和 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)。 问题注释中提供的示例为在 6.0 版最小 API发布之前面向 ASP.NET Core 的应用编写。 对于 6.0 或更高版本的应用,请将示例中的服务注册更改为Program.cs
的builder.Services.AddSingleton...
。
位置和持续时间
要启用缓存,Duration 必须设置为正值,并且 Location 必须为 Any
(默认值)或 Client
。 框架将 Cache-Control
标头设置为位置值,后跟响应的 max-age
。
Any
和 Client
的 Location 选项分别转换为 public
和 private
的 Cache-Control
标头值。 如 NoStore 和 Location.None 部分所述,将 Location 设置为 None
会将 Cache-Control
和 Pragma
标头设置为 no-cache
。
Location.Any
(Cache-Control
设置为 public
)指示客户端或任何中间代理可以缓存值,包括响应缓存中间件。
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 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());
}
其他资源
响应缓存可减少客户端或代理对 Web 服务器发出的请求数。 响应缓存还减少了 Web 服务器为生成响应而执行的工作量。 响应缓存由标头控制,这些标头指定你希望客户端、代理和中间件如何缓存响应。
[ResponseCache]
参与设置响应缓存标头。 客户端和中间代理应遵循RFC 9111:HTTP 缓存下缓存响应的标头。
对于遵循 HTTP 1.1 缓存规范的服务器端缓存,请使用响应缓存中间件。 中间件可以使用 [ResponseCache]
属性来设置服务器端缓存标头。
基于 HTTP 的响应缓存
RFC 9111:HTTP 缓存介绍了 Internet 缓存的行为方式。 用于缓存的主 HTTP 标头是 Cache-Control,它用于指定缓存指令。 当请求从客户端到达服务器以及响应从服务器返回客户端时,这些指令控制缓存行为。 请求和响应在代理服务器之间移动,并且代理服务器还必须符合 HTTP 1.1 缓存规范。
下表中显示了常用 Cache-Control
指令。
指令 | 操作 |
---|---|
公共 | 缓存可以存储响应。 |
private | 响应不得由共享缓存存储。 专用缓存可以存储和重用响应。 |
max-age | 客户端不接受期限大于指定秒数的响应。 示例:max-age=60 (60 秒),max-age=2592000 (1 个月) |
no-cache | 请求时:缓存不能使用存储的响应来满足请求。 源服务器重新生成客户端的响应,中间件更新其缓存中存储的响应。 响应时:响应不得用于未经源服务器验证的后续请求。 |
no-store | 请求时:缓存不得存储请求。 响应时:缓存不得存储任何部分的响应。 |
下表显示了在缓存中发挥了作用的其他缓存标头。
Header | 函数 |
---|---|
帐龄 | 在源服务器上生成或成功验证响应以来的估计时间量(以秒为单位)。 |
到期 | 响应被视为过时后的时间。 |
Pragma | 为与用于设置 no-cache 行为的 HTTP/1.0 缓存向后兼容而存在。 如果 Cache-Control 标头存在,则将忽略 Pragma 标头。 |
Vary | 指定除非所有 Vary 标头字段在缓存响应的原始请求和新请求中都匹配,否则不得发送缓存响应。 |
基于 HTTP 的缓存遵循请求 Cache-Control 指令
RFC 9111:HTTP 缓存(第 5.2 节. Cache-Control)要求缓存遵循客户端发送的有效Cache-Control
标头。 客户端可以使用 no-cache
标头值发出请求,并强制服务器针对每个请求生成新的响应。
如果考虑 HTTP 缓存的目标,则始终遵循客户端 Cache-Control
请求标头是有意义的。 根据官方规范,缓存旨在减少在客户端、代理和服务器网络中满足请求的延迟和网络开销。 它不一定是控制源服务器上的负载的一种方法。
使用响应缓存中间件时,开发人员无法控制此缓存行为,因为该中间件遵循官方缓存规范。 支持使用输出缓存以更好地控制服务器负载是 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 页面的内容。 缓存标记帮助程序使用内存中缓存来存储数据。
有关详细信息,请参阅 ASP.NET Core MVC 中的缓存标记帮助程序。
分布式缓存标记帮助程序
使用分布式缓存标记帮助程序在分布式云或 Web 场方案中缓存来自 MVC 视图或 Razor 页面的内容。 分布式缓存标记帮助程序使用 SQL Server、Redis 或 NCache 来存储数据。
有关详细信息,请参阅 ASP.NET Core 中的分布式缓存标记帮助程序。
ResponseCache 属性
ResponseCacheAttribute 指定在响应缓存中设置相应标头所需的参数。
警告
对包含经过身份验证的客户端信息的内容禁用缓存。 仅应为不会根据用户标识或用户是否登录而更改的内容启用缓存。
VaryByQueryKeys 根据给定的查询键列表的值改变存储的响应。 当提供了 *
的单个值时,中间件将根据所有请求查询字符串参数改变响应。
必须启用响应缓存中间件才能设置 VaryByQueryKeys 属性。 否则,会引发运行时异常。 VaryByQueryKeys 属性没有对应的 HTTP 标头。 属性是一个 HTTP 功能,由响应缓存中间件进行处理。 对于用于缓存响应的中间件,查询字符串和查询字符串值必须与上一个请求匹配。 例如,请考虑下表中所示的请求和结果的顺序。
请求 | 结果 |
---|---|
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 中设置的属性写出相应标头。
- 如果设置了 VaryByQueryKeys,则更新响应缓存 HTTP 功能。
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
标头设置为位置值,后跟响应的 max-age
。
Any
和 Client
的 Location 选项分别转换为 public
和 private
的 Cache-Control
标头值。 如 NoStore 和 Location.None 部分所述,将 Location 设置为 None
会将 Cache-Control
和 Pragma
标头设置为 no-cache
。
Location.Any
(Cache-Control
设置为 public
)指示客户端或任何中间代理可以缓存值,包括响应缓存中间件。
Location.Client
(Cache-Control
设置为 private
)指示只有客户端可以缓存值。 任何中间缓存都不应缓存值,包括响应缓存中间件。
缓存控制标头仅为客户端和中间代理提供有关何时以及如何缓存响应的指导。 不保证客户端和代理将遵循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