- 全球化与本土化
- 性能
- 高级
响应缓存在 ASP.NET Core 中的中间件
作者: Luke Latham和John Luo
此文章介绍了如何在 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(); 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(); }); }
示例应用添加标头以在后续请求时控制缓存:
// 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(); });
响应缓存中间件仅缓存服务器响应,导致了200(正常)状态代码。 中间件将忽略任何其他响应,包括错误页。
警告
包含经过身份验证的客户端的内容的响应必须标记为不可缓存,以防中间件存储和服务这些响应。 有关中间件如何确定响应是否可缓存的详细信息,请参阅缓存的条件。
Options
响应缓存选项如下表中所示。
选项 | 说明 |
---|---|
MaximumBodySize | 响应正文的最大可缓存大小(以字节为单位)。 默认值为 64 * 1024 * 1024 (64 MB)。 |
SizeLimit | 响应缓存中间件的大小限制(以字节为单位)。 默认值为 100 * 1024 * 1024 (100 MB)。 |
UseCaseSensitivePaths | 确定是否将响应缓存在区分大小写的路径上。 默认值是 false 。 |
下面的示例将中间件配置为:
- 大小小于或等于1024字节的缓存响应。
- 将响应存储为区分大小写的路径。 例如,
/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使用 ResponseCachingFeature:
var responseCachingFeature = context.HttpContext.Features.Get<IResponseCachingFeature>(); if (responseCachingFeature != null) { responseCachingFeature.VaryByQueryKeys = new[] { "MyKey" }; }
使用 VaryByQueryKeys
中 *
的单个值将按所有请求查询参数改变缓存。
响应缓存中间件使用的 HTTP 标头
下表提供了有关影响响应缓存的 HTTP 标头的信息。
标头 | 详细信息 |
---|---|
Authorization |
如果标头存在,则不会缓存响应。 |
Cache-Control |
中间件仅考虑用 public 缓存指令标记的缓存响应。 具有以下参数的控件缓存:
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。 请参阅以下主题:
故障排除
如果缓存行为与预期不符,请确认响应是可缓存的并且能够通过缓存提供服务。 检查请求的传入标头和响应的传出标头。 启用日志记录以帮助进行调试。
在对缓存行为进行测试和故障排除时,浏览器可能会以不需要的方式设置影响缓存的请求标头。 例如,浏览器可以将 Cache-Control
标题设置为刷新页面时 no-cache
或 max-age=0
。 以下工具可以显式设置请求标头,并优先于测试缓存:
缓存条件
- 请求必须导致服务器响应,状态代码为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 窗体元素禁用防伪标记的信息,请参阅 在 ASP.NET Core 防止跨站点请求伪造 (XSRF/CSRF) 攻击。
其他资源
此文章介绍了如何在 ASP.NET Core 应用程序中配置缓存响应的中间件。 中间件确定响应何时可缓存、存储响应,并提供来自缓存的响应。 有关 HTTP 缓存和[ResponseCache]
属性的介绍,请参阅响应缓存。
配置
使用AspNetCore 元包或添加对AspNetCore. ResponseCaching包的包引用。
在 Startup.ConfigureServices
中,将响应缓存中间件添加到服务集合:
public void ConfigureServices(IServiceCollection services) { services.AddResponseCaching(); services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); }
将应用程序配置为将中间件与 UseResponseCaching 扩展方法一起使用,该方法将中间件添加到 Startup.Configure
中的请求处理管道:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); } app.UseStaticFiles(); 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.UseMvc(); }
示例应用添加标头以在后续请求时控制缓存:
// 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(); });
响应缓存中间件仅缓存服务器响应,导致了200(正常)状态代码。 中间件将忽略任何其他响应,包括错误页。
警告
包含经过身份验证的客户端的内容的响应必须标记为不可缓存,以防中间件存储和服务这些响应。 有关中间件如何确定响应是否可缓存的详细信息,请参阅缓存的条件。
Options
响应缓存选项如下表中所示。
选项 | 说明 |
---|---|
MaximumBodySize | 响应正文的最大可缓存大小(以字节为单位)。 默认值为 64 * 1024 * 1024 (64 MB)。 |
SizeLimit | 响应缓存中间件的大小限制(以字节为单位)。 默认值为 100 * 1024 * 1024 (100 MB)。 |
UseCaseSensitivePaths | 确定是否将响应缓存在区分大小写的路径上。 默认值是 false 。 |
下面的示例将中间件配置为:
- 大小小于或等于1024字节的缓存响应。
- 将响应存储为区分大小写的路径。 例如,
/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使用 ResponseCachingFeature:
var responseCachingFeature = context.HttpContext.Features.Get<IResponseCachingFeature>(); if (responseCachingFeature != null) { responseCachingFeature.VaryByQueryKeys = new[] { "MyKey" }; }
使用 VaryByQueryKeys
中 *
的单个值将按所有请求查询参数改变缓存。
响应缓存中间件使用的 HTTP 标头
下表提供了有关影响响应缓存的 HTTP 标头的信息。
标头 | 详细信息 |
---|---|
Authorization |
如果标头存在,则不会缓存响应。 |
Cache-Control |
中间件仅考虑用 public 缓存指令标记的缓存响应。 具有以下参数的控件缓存:
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。 请参阅以下主题:
故障排除
如果缓存行为与预期不符,请确认响应是可缓存的并且能够通过缓存提供服务。 检查请求的传入标头和响应的传出标头。 启用日志记录以帮助进行调试。
在对缓存行为进行测试和故障排除时,浏览器可能会以不需要的方式设置影响缓存的请求标头。 例如,浏览器可以将 Cache-Control
标题设置为刷新页面时 no-cache
或 max-age=0
。 以下工具可以显式设置请求标头,并优先于测试缓存:
缓存条件
- 请求必须导致服务器响应,状态代码为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 窗体元素禁用防伪标记的信息,请参阅 在 ASP.NET Core 防止跨站点请求伪造 (XSRF/CSRF) 攻击。