本文介绍如何在 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
。
该示例应用程序添加了标头来控制后续请求的缓存:
Cache-Control
:最多可缓存10秒钟的可缓存响应。Vary
:仅在后续请求的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
时被覆盖:
- 具有[
ResponseCache
]属性。 即使未设置属性也是如此。 例如,省略VaryByHeader
属性将导致从响应中删除相应的标头。
响应缓存中间件仅缓存服务器响应,导致了200
(正常)状态代码。 中间件将忽略任何其他响应,包括错误页。
警告
包含经过身份验证的客户端的内容的响应必须标记为不可缓存,以防中间件存储和服务这些响应。
有关中间件如何确定响应是否可缓存的详细信息,请参阅缓存的条件。
选项
响应缓存选项如下表中所示
选项 | |
---|---|
选项 | 说明 |
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 标头的信息。
Header | 详细信息 |
---|---|
Authorization | 如果标头存在,则不会缓存响应。 |
Cache-Control | 中间件仅考虑标记有public cache指令的缓存响应。 使用以下参数控制缓存: |
max-age | |
max-stale† | |
min-fresh | |
must-revalidate | |
no-cache | |
no-store | |
only-if-cached | |
private | |
public | |
s-maxage | |
proxy-revalidate‡ | |
† 如果没有为max-stale 指定任何限制,则中间件不采取任何措施。 | |
‡ proxy-revalidate 与 must-revalidate 具有相同的效果。 | |
有关详细信息,请参阅RFC 7231:请求缓存控制指令。 | |
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: gzip Accept-Encoding: text/plain 。 永远不会存储标头值 * 为的响应。 |
Expires | 除非由其他标头重写,否则不会存储或检索此标头过时的响应 Cache-Control 。 |
If-None-Match | 如果值不为 * ,并且响应的与提供的任何值都不匹配,则将从缓存中提供完整响应 ETag 。 否则,将提供304 (未修改)响应。 |
If-Modified-Since | 如果 If-None-Match 标头不存在,则在缓存的响应日期比提供的值更新时,将从缓存中提供完整响应。 否则,将提供304 -未修改响应。 |
Date | 从缓存提供时, Date 如果未在原始响应中提供标头,中间件将设置标头。 |
Content-Length | 从缓存提供时, Content-Length 如果未在原始响应中提供标头,中间件将设置标头。 |
Age | Age忽略原始响应中发送的标头。 中间件在为缓存的响应提供服务时计算一个新值。 |
缓存遵从请求缓存控制指令
中间件遵守HTTP 1.1缓存规范的规则。 规则需要缓存来兑现客户端发送的有效Cache-Control
标头。 根据该规范,客户端可以使用no-cache
标头值发出请求,并强制服务器为每个请求生成新的响应。 当前,使用中间件时,开发人员无法控制这种缓存行为,因为中间件遵守官方缓存规范。
若要更好地控制缓存行为,请探索ASP.NET Core的其他缓存功能。 请参阅以下主题:
- ASP.NET Core 中的缓存内存
- ASP.NET Core 中的分布式缓存
- ASP.NET Core MVC 中的缓存标记帮助程序
- ASP.NET Core 中的分布式缓存标记帮助程序
疑难解答
如果缓存行为与预期的不同,请确认响应是可缓存的并且能够从缓存中得到响应。 检查请求的传入标头和响应的传出标头。 启用日志记录以帮助调试。
在对缓存行为进行测试和故障排除时,浏览器可能会设置以不希望的方式影响缓存的请求标头。 例如,当刷新页面时,浏览器可以将Cache-Control
标头设置为no-cache
或max-age = 0
。 以下工具可以显式设置请求标头,并且是测试缓存的首选工具:
- Fiddler
- Postman
缓存条件
- 请求必须导致服务器响应,状态代码为
200
(正常)。 - 请求方法必须为
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-age
和s-maxage
缓存指令所指定。 - 响应缓冲必须成功。 响应的大小必须小于配置的或默认值
SizeLimit
。 响应的正文大小必须小于配置的或默认值
MaximumBodySize
。 - 必须根据
RFC 7234
规范来缓存响应。 例如,no-store
指令在请求或响应标头字段中不得存在。
有关详细信息,请参见第3节:在RFC 7234
的缓存中存储响应。
备注
用于生成安全令牌以防止跨站点请求伪造(CSRF
)攻击的防伪系统将Cache-Control
和Pragma
标头设置为no-cache
,这样就不会缓存响应。 有关如何为HTML表单元素禁用防伪令牌的信息,请参见阻止跨站点请求伪造(XSRF / CSRF)攻击ASP.NET Core
。