ASP.NET Core 中的响应缓存中间件 港控/mmm° 2021-07-04 11:36 365阅读 0赞 本文介绍如何在 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`(正常)状态代码。 中间件将忽略任何其他响应,包括错误页。 > 警告 > > 包含经过身份验证的客户端的内容的响应必须标记为不可缓存,以防中间件存储和服务这些响应。 > 有关中间件如何确定响应是否可缓存的详细信息,请参阅缓存的条件。 ## 选项 ## 响应缓存选项如下表中所示 <table> <thead> <tr> <th>选项</th> <th></th> </tr> </thead> <tbody> <tr> <td>选项</td> <td>说明</td> </tr> <tr> <td><code>MaximumBodySize</code></td> <td>响应正文的最大可缓存大小(以字节为单位)。 默认值为 64 * 1024 * 1024 (64 MB)。</td> </tr> <tr> <td><code>SizeLimit</code></td> <td>响应缓存中间件的大小限制(以字节为单位)。 默认值为 100 * 1024 * 1024 (100 MB)。</td> </tr> <tr> <td><code>UseCaseSensitivePaths</code></td> <td>确定是否将响应缓存在区分大小写的路径上。 默认值为 false。</td> </tr> </tbody> </table> 以下示例将中间件配置为: * 缓存正文大小小于或等于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 标头的信息。 <table> <thead> <tr> <th>Header</th> <th>详细信息</th> </tr> </thead> <tbody> <tr> <td><code>Authorization</code></td> <td>如果标头存在,则不会缓存响应。</td> </tr> <tr> <td><code>Cache-Control</code></td> <td>中间件仅考虑标记有<code>public</code> cache指令的缓存响应。 使用以下参数控制缓存:</td> </tr> <tr> <td></td> <td>max-age</td> </tr> <tr> <td></td> <td>max-stale†</td> </tr> <tr> <td></td> <td>min-fresh</td> </tr> <tr> <td></td> <td>must-revalidate</td> </tr> <tr> <td></td> <td>no-cache</td> </tr> <tr> <td></td> <td>no-store</td> </tr> <tr> <td></td> <td>only-if-cached</td> </tr> <tr> <td></td> <td>private</td> </tr> <tr> <td></td> <td>public</td> </tr> <tr> <td></td> <td>s-maxage</td> </tr> <tr> <td></td> <td>proxy-revalidate‡</td> </tr> <tr> <td></td> <td><code>†</code>如果没有为<code>max-stale</code>指定任何限制,则中间件不采取任何措施。</td> </tr> <tr> <td></td> <td><code>‡</code> <code>proxy-revalidate</code>与 <code>must-revalidate</code> 具有相同的效果。</td> </tr> <tr> <td></td> <td>有关详细信息,请参阅<a href="https://tools.ietf.org/html/rfc7234#section-5.2.1" rel="nofollow">RFC 7231:请求缓存控制指令</a>。</td> </tr> <tr> <td><code>Pragma</code></td> <td><code>Pragma: no-cache</code>请求中的标头将产生与相同的效果 <code>Cache-Control: no-cache</code> 。 标头中的相关指令 <code>Cache-Control</code> (如果存在)将重写此标头。 考虑向后兼容 <code>HTTP/1.0</code>。</td> </tr> <tr> <td><code>Set-Cookie</code></td> <td>如果标头存在,则不会缓存响应。 请求处理管道中设置一个或多个 cookie 的任何中间件会阻止响应缓存中间件缓存响应(例如,基于 cookie 的 <code>TempData</code> 提供程序)。</td> </tr> <tr> <td><code>Vary</code></td> <td><code>Vary</code>标头用于根据另一个标头改变缓存的响应。 例如,通过包含标头来缓存响应, <code>Vary: Accept-Encoding</code> 此标头将使用标头和单独的请求来缓存响应 <code>Accept-Encoding: gzip</code> <code>Accept-Encoding: text/plain</code> 。 永远不会存储标头值 * 为的响应。</td> </tr> <tr> <td><code>Expires</code></td> <td>除非由其他标头重写,否则不会存储或检索此标头过时的响应 <code>Cache-Control</code> 。</td> </tr> <tr> <td><code>If-None-Match</code></td> <td>如果值不为 <code>*</code> ,并且响应的与提供的任何值都不匹配,则将从缓存中提供完整响应 <code>ETag</code> 。 否则,将提供<code>304</code>(未修改)响应。</td> </tr> <tr> <td><code>If-Modified-Since</code></td> <td>如果 <code>If-None-Match</code> 标头不存在,则在缓存的响应日期比提供的值更新时,将从缓存中提供完整响应。 否则,将提供<code>304</code>-未修改响应。</td> </tr> <tr> <td><code>Date</code></td> <td>从缓存提供时, <code>Date</code> 如果未在原始响应中提供标头,中间件将设置标头。</td> </tr> <tr> <td><code>Content-Length</code></td> <td>从缓存提供时, <code>Content-Length</code> 如果未在原始响应中提供标头,中间件将设置标头。</td> </tr> <tr> <td><code>Age</code></td> <td>Age忽略原始响应中发送的标头。 中间件在为缓存的响应提供服务时计算一个新值。</td> </tr> </tbody> </table> ## 缓存遵从请求缓存控制指令 ## 中间件遵守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`。
还没有评论,来说两句吧...