ASP.Net Core 2.0 - ResponseCaching 中间件 - 不在服务器上缓存

Posted

技术标签:

【中文标题】ASP.Net Core 2.0 - ResponseCaching 中间件 - 不在服务器上缓存【英文标题】:ASP.Net Core 2.0 - ResponseCaching Middleware - Not Caching on Server 【发布时间】:2018-06-22 07:24:14 【问题描述】:

我想在 asp.net core 2.0 中使用服务器端响应缓存(输出缓存),发现了 Response Caching Middleware 并想尝试一个全新的 asp.core mvc 项目。

这是上面链接中的描述,这让我觉得这可以像输出缓存一样使用。

中间件确定响应何时可缓存、存储响应并从缓存中提供响应。

这是我的 startup.cs 的样子。

public class Startup

    public Startup(IConfiguration configuration)
    
        Configuration = configuration;
    

    public IConfiguration Configuration  get; 

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    
        services.AddResponseCaching();
        services.AddMvc();
    

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    
        app.UseResponseCaching();

        if (env.IsDevelopment())
        
            app.UseBrowserLink();
            app.UseDeveloperExceptionPage();
        
        else
        
            app.UseExceptionHandler("/Home/Error");
        

        app.UseStaticFiles();

        app.UseMvc(routes =>
        
            routes.MapRoute(
                name: "default",
                template: "controller=Home/action=Index/id?");
        );
    

这里是 HomeController.cs

[ResponseCache(Duration = 60)]
public class HomeController : Controller

    public IActionResult Index()
    
        return View();
    

    public IActionResult About()
    
        ViewData["Message"] = "Your application description page.";

        return View();
    

    public IActionResult Contact()
    
        ViewData["Message"] = "Your contact page.";

        return View();
    

    public IActionResult Error()
    
        return View(new ErrorViewModel  RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier );
    

_Layout.cshtml 文件的底部还有一个时间戳,因此我可以知道页面何时呈现,如下所示。

<p>&copy; 2018 - ResponseCachingMiddleware - @DateTime.UtcNow</p>

Cache-Control 标头似乎很好,这是我在加载页面时在标头中得到的,但时间戳会在每秒每次刷新时不断更新。

Cache-Control:public,max-age=60

我从 MS 文档中了解到的是响应缓存中间件是负责缓存响应的服务器端缓存机制,而 Response Caching 似乎只是一个过滤器来操纵响应标头以进行缓存。

无法判断我的理解或代码是否有问题,我想抱怨自从我开始使用 ASP.Net Core 进行原型设计以来,我经常有这种感觉。也许您还可以建议更好的资源作为副主题。

我之前看过这篇文章 ASP.NET Core 2.0 - Http Response Caching Middleware - Nothing cached

也检查了这一点,但似乎唯一的区别是我使用的是 mvc。 https://github.com/aspnet/ResponseCaching/blob/dev/samples/ResponseCachingSample/Startup.cs

谢谢

编辑:我在输出窗口中看到下面的消息,除了我已经检查过响应缓存中间件的几个地方之外,在谷歌上找不到任何关于它的信息。

Microsoft.AspNetCore.ResponseCaching.ResponseCachingMiddleware:信息: 无法缓存此请求的响应。

注意:我希望我可以创建#response-caching-middleware 标签。不确定#responsecache 是否相关。

【问题讨论】:

【参考方案1】:

我遇到了同样的问题,我正要大发雷霆,我会设置 app.UseResponseCaching();services.AddResponseCaching(); 并在我的操作顶部添加 ResponseCache,就像微软官方所说的一样文档,尽管在从服务器返回的响应中正确设置了 cache-controll 标头,但在服务器端仍然没有缓存任何内容。

在这个问题上花费了几个小时后,我弄清楚了问题出在哪里以及为什么没有缓存在服务器上。

浏览器默认将请求的cache-controll 值设置为max-age=0(如果请求不是由后退或前进引起的),即使您通过在顶部添加ResponseCache 属性在响应中正确设置了cache-controller您的操作(或控制器)由于请求发送的cache-controller 设置为max-age=0,服务器无法缓存响应,我认为这也必须添加到 响应缓存 限制列表

无论如何,您可以通过在调用 app.UseResponseCaching(); 之前添加几行代码来覆盖浏览器默认行为,另一方面,您需要在调用 app.UseResponseCaching(); 之前添加自定义中间件来修改请求 cache-control 标头值。

查看下面的代码,对我有用,希望对你也有用

 app.Use(async (ctx, next) =>
        
            ctx.Request.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
            
                Public = true,
                MaxAge = TimeSpan.FromSeconds(60)
            ;
            await next();
        
            );
        app.UseResponseCaching();

为确保 ResponseCaching 按预期工作,您也可以使用邮递员,但您必须在设置中将 'Send no-cache Header' 设置为 off,见下图

【讨论】:

谢谢,我已经放弃这个问题一段时间了,但我会尽快回复并尝试一下。 @Engin 快乐编码 :) 是的,它已经工作了,如果有人有编译错误添加他的命名空间 Microsoft.AspNetCore.Http GetTypedHeaders() 是一种扩展方法 适用于 web api 调用,注意你可以检查输出窗口(我是 .net core 3.1):“ResponseCachingMiddleware: Information: The response has been cached.”, “ResponseCachingMiddleware: Information: Serving来自缓存的响应。”【参考方案2】:

我最近也有同样的困惑。

ASP.Net Core 的 ResponseCaching 确实提供了客户端缓存(通过 HTTP 响应标头)和服务器端(通过内存缓存的中间件,如果响应在缓存中,该中间件会短路其他中间件)。服务器端部分读取 HTTP 响应缓存标头以确定它是否应该进行服务器端缓存(类似于 ISP 或 CDN 可能会做的事情)。

不幸的是,调试服务器端 ResponseCaching 很棘手,因为它有奇怪的规则并且没有足够的日志记录。在我的例子中,我下载了 Microsoft 的源代码以单步执行它并找到我的代码的问题。

您在输出窗口中找到的“无法缓存此请求的响应”的注释是一个线索。

请求的服务器端缓存有 2 个部分。服务器必须在第一次请求 url 时填充缓存。它将在第二次请求时提供缓存版本。注意错误消息何时出现,如果它是在第一个或第二个请求上。这将告诉您它是否无法存储在缓存中,或者是否无法从缓存中检索。

存储和检索的规则都在这个源代码文件中: https://github.com/aspnet/ResponseCaching/blob/3bf5f6a1ce69b65c998d6f5c739822a9bed4a67e/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingPolicyProvider.cs

您的“Cache-Control:public,max-age=60”标头应该符合这些规则就好了。

我的猜测是你确实让它工作了,但不知道如何正确测试它。 本期提到的 ResponseCaching 有一个违反直觉的部分:https://github.com/aspnet/Home/issues/2607 本质上,如果浏览器发送 no-cache 或 no-store 标头(当您按下 CTRL+F5 或打开调试器工具时),ASP.Net Core 的 ResponseCaching 将接受浏览器的请求并重新生成响应。

因此,为了测试您的代码是否正常工作,您可能加载了已启动缓存的页面,然后您按 CTRL+F5 强制刷新您的浏览器,并且您希望服务器端响应缓存条目而不是运行您的 WebAPI 代码。但是,它尊重无缓存请求标头并绕过缓存(并在您的输出日志中写入该消息)。

对此进行测试的方法是清除请求之间的浏览器缓存(或切换到隐身模式),而不是使用 CTRL+F5。

附带说明,尊重 no-cache/no-store 请求标头可能是一个糟糕的设计选择,因为 ASP.Net Core 的 ResponseCache 很可能由拥有响应的服务器使用,而不是像中间缓存一样CDN/ISP。我已经扩展了基本的 ResponseCache 选项,以禁用尊重这些标头(以及将缓存序列化到磁盘,而不是仅在内存中)。它是默认缓存的简单替代品。

你可以在这里找到我的扩展: https://github.com/speige/AspNetCore.ResponseCaching.Extensions https://www.nuget.org/packages/AspNetCore.ResponseCaching.Extensions

还有其他一些关于 ResponseCaching 的问题需要注意,您可能已经在您发布的博客网址中阅读过这些问题。使用 set-cookie 验证的请求和响应不会被缓存。只有使用 GET 或 HEAD 方法的请求才会被缓存。如果 QueryString 不同,它将创建一个新的缓存条目。此外,如果请求的某些条件与先前缓存的请求不同(例如:user-agent、accept-encoding 等),通常您需要一个“Vary”标头来防止缓存。最后,如果一个中间件处理一个请求,它会短路后面的中间件。确保您的 app.UseResponseCaching() 在 app.UseMVC() 之前注册

【讨论】:

感谢您的详细回复。在找到不同的解决方案后,我已经放弃了这个问题一段时间,但我会尽快检查一下。比起功能不存在,我测试错了更有意义。 @DevinGarner 查看了您的代码,您应该记录异常(因为有)。顺便说一句,与默认响应缓存相比,您应该对代码进行性能基准测试。你的实现很慢 :) 并且应该支持任何 IResponseCacheEntry。问候。 感谢@Fab 的代码审查,如果您有时间,请提交包含您的改进的拉取请求。谢谢! 谢天谢地,我一直读到你的回答。在app.UseMvc(); 之前调用app.UseResponseCaching(); 成功了! Microsoft 迫切需要告诉我们违反了哪条阻止缓存的规则。【参考方案3】:

如果Cache-Control 标头通过,那么它正在工作。从这个角度来看,这就是服务器所能做的一切。客户端最终决定是否实际缓存资源。发送标头不会强制客户端做任何事情;事实上,服务器通常不能强迫客户端做任何事情。

【讨论】:

您能解释一下响应缓存中间件和响应缓存之间的区别吗?据我所知,您描述的是响应缓存过滤器的职责。 没有。它们是同一枚硬币的两个面。中间件是实际工作,而属性则通知中间件它应该实际做什么(即设置max-age=60,因为您指定了Duration = 60)。一般来说,响应缓存只设置Cache-Control 标头。它对缓存响应服务器端没有任何作用。而且,就像我说的那样,是否真正尊重标头是由客户 100% 决定的。如果您正在寻找服务器端缓存,则需要使用内存缓存或分布式缓存。 请参阅我参考的文档页面中对响应缓存中间件的描述。它清楚地表明它存储并提供响应。您可能会将其与响应缓存混淆。 这个答案不正确。 ASP.Net Core 中的 ResponseCaching 执行客户端缓存(通过标头)和服务器端缓存(类似于以前版本的 ASP.Net 中的 OutputCache)。 OP 专门询问为什么服务器端缓存不起作用。 @ChrisPratt 我已经阅读了源代码。 github.com/aspnet/ResponseCaching我也成功使用了。您可能对 .net 核心的 ResponseCaching 的早期版本是正确的,但它也已成为服务器端缓存已有一年多了。

以上是关于ASP.Net Core 2.0 - ResponseCaching 中间件 - 不在服务器上缓存的主要内容,如果未能解决你的问题,请参考以下文章

小5聊asp.net和asp.net core不同点积累

带你做 WebAPI 迁移 ASP.NET Core 2.0

ASP.Net Core 2.0 FileTable 使用

ASP.NET Core 2.0 中的多个身份

ASP.NET Core 2.0 : 八.图说管道

ASP.NET CORE 2.0 中的 FromUri