响应缓存在 ASP.NET Core 中的中间件Response Caching Middleware in ASP.NET Core

本文内容

作者:John Luo

此文章介绍了如何在 ASP.NET Core 应用程序中配置缓存响应的中间件。中间件确定响应何时可缓存、存储响应,并提供来自缓存的响应。有关 HTTP 缓存和[ResponseCache]属性的介绍,请参阅响应缓存

查看或下载示例代码如何下载

配置Configuration

响应缓存中间件可通过共享框架隐式地用于 ASP.NET Core 应用。

Startup.ConfigureServices中,将响应缓存中间件添加到服务集合:

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddResponseCaching();
  4. services.AddRazorPages();
  5. }

将应用程序配置为将中间件与 UseResponseCaching 扩展方法一起使用,该方法将中间件添加到 Startup.Configure中的请求处理管道:

  1. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  2. {
  3. if (env.IsDevelopment())
  4. {
  5. app.UseDeveloperExceptionPage();
  6. }
  7. else
  8. {
  9. app.UseExceptionHandler("/Error");
  10. }
  11. app.UseStaticFiles();
  12. app.UseRouting();
  13. app.UseResponseCaching();
  14. app.Use(async (context, next) =>
  15. {
  16. context.Response.GetTypedHeaders().CacheControl =
  17. new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
  18. {
  19. Public = true,
  20. MaxAge = TimeSpan.FromSeconds(10)
  21. };
  22. context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] =
  23. new string[] { "Accept-Encoding" };
  24. await next();
  25. });
  26. app.UseEndpoints(endpoints =>
  27. {
  28. endpoints.MapRazorPages();
  29. });
  30. }

示例应用添加标头以在后续请求时控制缓存:

  • 缓存控制– 将可缓存的响应缓存多达10秒。
  • 不同– 将中间件配置为仅当后续请求的 Accept 编码标头与原始请求的接受编码标头匹配时才提供缓存的响应。
  1. // using Microsoft.AspNetCore.Http;
  2. app.Use(async (context, next) =>
  3. {
  4. context.Response.GetTypedHeaders().CacheControl =
  5. new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
  6. {
  7. Public = true,
  8. MaxAge = TimeSpan.FromSeconds(10)
  9. };
  10. context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] =
  11. new string[] { "Accept-Encoding" };
  12. await next();
  13. });

响应缓存中间件仅缓存服务器响应,导致了200(正常)状态代码。中间件将忽略任何其他响应,包括错误页

警告

包含经过身份验证的客户端的内容的响应必须标记为不可缓存,以防中间件存储和服务这些响应。有关中间件如何确定响应是否可缓存的详细信息,请参阅缓存的条件

选项Options

响应缓存选项如下表中所示。

选项说明
MaximumBodySize响应正文的最大可缓存大小(以字节为单位)。默认值为 64 1024 1024 (64 MB)。
SizeLimit响应缓存中间件的大小限制(以字节为单位)。默认值为 100 1024 1024 (100 MB)。
UseCaseSensitivePaths确定是否将响应缓存在区分大小写的路径上。默认值是 false

下面的示例将中间件配置为:

  • 大小小于或等于1024字节的缓存响应。
  • 将响应存储为区分大小写的路径。例如,/page1/Page1 单独存储。
  1. services.AddResponseCaching(options =>
  2. {
  3. options.MaximumBodySize = 1024;
  4. options.UseCaseSensitivePaths = true;
  5. });

VaryByQueryKeysVaryByQueryKeys

使用 MVC/web API 控制器或 Razor Pages 页面模型时, [ResponseCache]属性指定为响应缓存设置适当的标头所需的参数。严格需要中间件的 [ResponseCache] 属性的唯一参数 VaryByQueryKeys,这与实际 HTTP 标头不对应。有关详细信息,请参阅 响应缓存在 ASP.NET Core

如果不使用 [ResponseCache] 属性,响应缓存可能会与 VaryByQueryKeys不同。直接从HttpContext使用 ResponseCachingFeature

  1. var responseCachingFeature = context.HttpContext.Features.Get<IResponseCachingFeature>();
  2. if (responseCachingFeature != null)
  3. {
  4. responseCachingFeature.VaryByQueryKeys = new[] { "MyKey" };
  5. }

使用 VaryByQueryKeys* 的单个值将按所有请求查询参数改变缓存。

响应缓存中间件使用的 HTTP 标头HTTP headers used by Response Caching Middleware

下表提供了有关影响响应缓存的 HTTP 标头的信息。

标头详细信息
Authorization如果标头存在,则不会缓存响应。
Cache-Control中间件仅考虑用 public 缓存指令标记的缓存响应。具有以下参数的控件缓存:

- max-age
- max-stale†
- 最小-新
- must-revalidate
- no-cache
- 无-商店
- 仅限-缓存
- 专用
- 公共
- s-maxage
- proxy-revalidate‡
†如果没有指定 max-stale的限制,则中间件不会执行任何操作。proxy-revalidatemust-revalidate的效果相同。有关详细信息,请参阅RFC 7231:请求缓存控制指令
Pragma请求中的 Pragma: no-cache 标头将产生与 Cache-Control: no-cache相同的效果。如果存在此标头,则由 Cache-Control 标头中的相关指令重写。考虑向后兼容 HTTP/1.0。
Set-Cookie如果标头存在,则不会缓存响应。请求处理管道中设置一个或多个 cookie 的任何中间件会阻止响应缓存中间件缓存响应(例如,基于 cookie 的 TempData 提供程序)。
VaryVary 标头用于根据另一个标头改变缓存的响应。例如,通过编码来缓存响应,包括 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 标头。
Content-Length从缓存提供时,如果未在原始响应中提供,则中间件会设置 Content-Length 标头。
Age忽略原始响应中发送的 Age 标头。中间件在为缓存的响应提供服务时计算一个新值。

缓存遵从请求缓存控制指令Caching respects request Cache-Control directives

中间件遵循HTTP 1.1 缓存规范的规则。规则要求使用缓存来服从客户端发送的有效 Cache-Control 标头。在规范下,客户端可以使用 no-cache 标头值发出请求,并强制服务器为每个请求生成新的响应。目前,在使用中间件时,不存在对此缓存行为的开发人员控制,因为中间件遵循官方缓存规范。

为了更好地控制缓存行为,将介绍其他缓存功能的 ASP.NET Core。请参阅下列主题:

故障排除Troubleshooting

如果缓存行为与预期不符,请确认响应是可缓存的并且能够通过缓存提供服务。检查请求的传入标头和响应的传出标头。启用日志记录以帮助进行调试。

在对缓存行为进行测试和故障排除时,浏览器可能会以不需要的方式设置影响缓存的请求标头。例如,浏览器可以将 Cache-Control 标题设置为刷新页面时 no-cachemax-age=0以下工具可以显式设置请求标头,并优先于测试缓存:

缓存条件Conditions for caching

  • 请求必须导致服务器响应,状态代码为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-ages-maxage 缓存指令指定的响应不能过时。
  • 响应缓冲必须成功。响应的大小必须小于配置的或默认 SizeLimit。响应的正文大小必须小于配置的或默认的 MaximumBodySize
  • 必须根据RFC 7234规范来缓存响应。例如,"请求" 或 "响应" 标头字段中不得存在 "no-store" 指令。有关详细信息,请参阅第3部分:将响应存储在RFC 7234的缓存中。

备注

用于生成安全令牌以防止跨站点请求伪造(CSRF)攻击的防伪系统将 Cache-ControlPragma 标头设置为 no-cache,以便不缓存响应。有关如何为 HTML 窗体元素禁用防伪标记的信息,请参阅 在 ASP.NET Core 防止跨站点请求伪造 (XSRF/CSRF) 攻击

其他资源Additional resources

此文章介绍了如何在 ASP.NET Core 应用程序中配置缓存响应的中间件。中间件确定响应何时可缓存、存储响应,并提供来自缓存的响应。有关 HTTP 缓存和[ResponseCache]属性的介绍,请参阅响应缓存

查看或下载示例代码如何下载

配置Configuration

使用AspNetCore 元包或添加对AspNetCore. ResponseCaching包的包引用。

Startup.ConfigureServices中,将响应缓存中间件添加到服务集合:

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddResponseCaching();
  4. services.AddMvc()
  5. .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
  6. }

将应用程序配置为将中间件与 UseResponseCaching 扩展方法一起使用,该方法将中间件添加到 Startup.Configure中的请求处理管道:

  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  2. {
  3. if (env.IsDevelopment())
  4. {
  5. app.UseDeveloperExceptionPage();
  6. }
  7. else
  8. {
  9. app.UseExceptionHandler("/Error");
  10. }
  11. app.UseStaticFiles();
  12. app.UseResponseCaching();
  13. app.Use(async (context, next) =>
  14. {
  15. context.Response.GetTypedHeaders().CacheControl =
  16. new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
  17. {
  18. Public = true,
  19. MaxAge = TimeSpan.FromSeconds(10)
  20. };
  21. context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] =
  22. new string[] { "Accept-Encoding" };
  23. await next();
  24. });
  25. app.UseMvc();
  26. }

示例应用添加标头以在后续请求时控制缓存:

  • 缓存控制– 将可缓存的响应缓存多达10秒。
  • 不同– 将中间件配置为仅当后续请求的 Accept 编码标头与原始请求的接受编码标头匹配时才提供缓存的响应。
  1. // using Microsoft.AspNetCore.Http;
  2. app.Use(async (context, next) =>
  3. {
  4. context.Response.GetTypedHeaders().CacheControl =
  5. new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
  6. {
  7. Public = true,
  8. MaxAge = TimeSpan.FromSeconds(10)
  9. };
  10. context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] =
  11. new string[] { "Accept-Encoding" };
  12. await next();
  13. });

响应缓存中间件仅缓存服务器响应,导致了200(正常)状态代码。中间件将忽略任何其他响应,包括错误页

警告

包含经过身份验证的客户端的内容的响应必须标记为不可缓存,以防中间件存储和服务这些响应。有关中间件如何确定响应是否可缓存的详细信息,请参阅缓存的条件

选项Options

响应缓存选项如下表中所示。

选项说明
MaximumBodySize响应正文的最大可缓存大小(以字节为单位)。默认值为 64 1024 1024 (64 MB)。
SizeLimit响应缓存中间件的大小限制(以字节为单位)。默认值为 100 1024 1024 (100 MB)。
UseCaseSensitivePaths确定是否将响应缓存在区分大小写的路径上。默认值是 false

下面的示例将中间件配置为:

  • 大小小于或等于1024字节的缓存响应。
  • 将响应存储为区分大小写的路径。例如,/page1/Page1 单独存储。
  1. services.AddResponseCaching(options =>
  2. {
  3. options.MaximumBodySize = 1024;
  4. options.UseCaseSensitivePaths = true;
  5. });

VaryByQueryKeysVaryByQueryKeys

使用 MVC/web API 控制器或 Razor Pages 页面模型时, [ResponseCache]属性指定为响应缓存设置适当的标头所需的参数。严格需要中间件的 [ResponseCache] 属性的唯一参数 VaryByQueryKeys,这与实际 HTTP 标头不对应。有关详细信息,请参阅 响应缓存在 ASP.NET Core

如果不使用 [ResponseCache] 属性,响应缓存可能会与 VaryByQueryKeys不同。直接从HttpContext使用 ResponseCachingFeature

  1. var responseCachingFeature = context.HttpContext.Features.Get<IResponseCachingFeature>();
  2. if (responseCachingFeature != null)
  3. {
  4. responseCachingFeature.VaryByQueryKeys = new[] { "MyKey" };
  5. }

使用 VaryByQueryKeys* 的单个值将按所有请求查询参数改变缓存。

响应缓存中间件使用的 HTTP 标头HTTP headers used by Response Caching Middleware

下表提供了有关影响响应缓存的 HTTP 标头的信息。

标头详细信息
Authorization如果标头存在,则不会缓存响应。
Cache-Control中间件仅考虑用 public 缓存指令标记的缓存响应。具有以下参数的控件缓存:

- max-age
- max-stale†
- 最小-新
- must-revalidate
- no-cache
- 无-商店
- 仅限-缓存
- 专用
- 公共
- s-maxage
- proxy-revalidate‡
†如果没有指定 max-stale的限制,则中间件不会执行任何操作。proxy-revalidatemust-revalidate的效果相同。有关详细信息,请参阅RFC 7231:请求缓存控制指令
Pragma请求中的 Pragma: no-cache 标头将产生与 Cache-Control: no-cache相同的效果。如果存在此标头,则由 Cache-Control 标头中的相关指令重写。考虑向后兼容 HTTP/1.0。
Set-Cookie如果标头存在,则不会缓存响应。请求处理管道中设置一个或多个 cookie 的任何中间件会阻止响应缓存中间件缓存响应(例如,基于 cookie 的 TempData 提供程序)。
VaryVary 标头用于根据另一个标头改变缓存的响应。例如,通过编码来缓存响应,包括 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 标头。
Content-Length从缓存提供时,如果未在原始响应中提供,则中间件会设置 Content-Length 标头。
Age忽略原始响应中发送的 Age 标头。中间件在为缓存的响应提供服务时计算一个新值。

缓存遵从请求缓存控制指令Caching respects request Cache-Control directives

中间件遵循HTTP 1.1 缓存规范的规则。规则要求使用缓存来服从客户端发送的有效 Cache-Control 标头。在规范下,客户端可以使用 no-cache 标头值发出请求,并强制服务器为每个请求生成新的响应。目前,在使用中间件时,不存在对此缓存行为的开发人员控制,因为中间件遵循官方缓存规范。

为了更好地控制缓存行为,将介绍其他缓存功能的 ASP.NET Core。请参阅下列主题:

故障排除Troubleshooting

如果缓存行为与预期不符,请确认响应是可缓存的并且能够通过缓存提供服务。检查请求的传入标头和响应的传出标头。启用日志记录以帮助进行调试。

在对缓存行为进行测试和故障排除时,浏览器可能会以不需要的方式设置影响缓存的请求标头。例如,浏览器可以将 Cache-Control 标题设置为刷新页面时 no-cachemax-age=0以下工具可以显式设置请求标头,并优先于测试缓存:

缓存条件Conditions for caching

  • 请求必须导致服务器响应,状态代码为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-ages-maxage 缓存指令指定的响应不能过时。
  • 响应缓冲必须成功。响应的大小必须小于配置的或默认 SizeLimit。响应的正文大小必须小于配置的或默认的 MaximumBodySize
  • 必须根据RFC 7234规范来缓存响应。例如,"请求" 或 "响应" 标头字段中不得存在 "no-store" 指令。有关详细信息,请参阅第3部分:将响应存储在RFC 7234的缓存中。

备注

用于生成安全令牌以防止跨站点请求伪造(CSRF)攻击的防伪系统将 Cache-ControlPragma 标头设置为 no-cache,以便不缓存响应。有关如何为 HTML 窗体元素禁用防伪标记的信息,请参阅 在 ASP.NET Core 防止跨站点请求伪造 (XSRF/CSRF) 攻击

其他资源Additional resources