【问题标题】:How to set different Cache expire times for Client and Server caches如何为客户端和服务器缓存设置不同的缓存过期时间
【发布时间】:2013-01-27 04:46:39
【问题描述】:

我希望某些页面有 10 分钟的客户端缓存和 24 小时的服务器缓存。原因是如果页面发生变化,客户端将在 10 分钟内获取更新的版本,但如果没有任何变化,服务器只需每天重建一次页面。

问题在于输出缓存设置似乎覆盖了客户端设置。这是我的设置:

自定义 ActionFilterAttribute 类

public class ClientCacheAttribute : ActionFilterAttribute
{
    private bool _noClientCache;

    public int ExpireMinutes { get; set; }

    public ClientCacheAttribute(bool noClientCache) 
    {
        _noClientCache = noClientCache;
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        if (_noClientCache || ExpireMinutes <= 0)
        {
            filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
            filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
            filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
            filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            filterContext.HttpContext.Response.Cache.SetNoStore();
        }
        else
        {
            filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(ExpireMinutes));
        }

        base.OnResultExecuting(filterContext);
    }
}

网络配置设置

  <outputCacheSettings>
    <outputCacheProfiles>
      <add name="Cache24Hours" location="Server" duration="86400" varyByParam="none" />
    </outputCacheProfiles>
  </outputCacheSettings>

我怎么称呼它:

[OutputCache(CacheProfile = "Cache24Hours")]
[ClientCacheAttribute(false, ExpireMinutes = 10)]
public class HomeController : Controller
{
  [...]
}

但是查看 HTTP Header 显示:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Expires: -1

我怎样才能正确地实现它?它是一个 ASP.NET MVC 4 应用程序。

【问题讨论】:

  • 我不熟悉这种缓存语法。我使用持续时间。成功了。这无助于调试你的,但它是一个替代建议

标签: asp.net asp.net-mvc caching outputcache


【解决方案1】:

您需要为服务器端缓存和客户端缓存实现自己的解决方案,使用 ClientCacheAttribute 或 OutputCache。以下是您需要自定义服务器端缓存解决方案的原因。

  • ClientCacheAttribute 将缓存策略设置为 Response.Cache,它是 HttpCachePolicyBase 的类型
  • 内置OutputCache也将缓存策略设置为Response.Cache

这里我想强调的是we don't have collection of HttpCachePolicyBase but we only have one object of HttpCachePolicyBase so we can't set multiple cache policy for given response.

即使我们可以将 Http Cacheability 设置为 HttpCacheability.ServerAndPrivate 但同样您将在缓存持续时间的其他问题中运行(即客户端 10 分钟和服务器 24 小时)

我的建议是使用 OutputCache 进行客户端缓存,并为服务器端缓存实现自己的缓存机制。

【讨论】:

  • 我试图避免这种情况,创建自定义客户端(基本上管理 http 标头)要容易得多,然后创建自定义服务器端解决方案,因为您必须管理页面副本缓存。不幸的是,当我尝试让 OutputCache 只管理服务器缓存时,它覆盖了我为客户端手动设置的 http 标头。您知道在服务器模式下覆盖输出缓存设置的 http 标头的方法吗?
  • Josh 这很可能会发生,这也很明显,因为逻辑上结果在结果执行后被缓存,而在大多数情况下,我们从操作方法或结果执行之前管理标头
  • 这就是为什么我建议将 OutputCache 用于客户端,因为不建议仅设置响应标头,而是使用缓存策略进行客户端缓存。
【解决方案2】:

在您的 OutputCache 配置文件中,将位置设置为 ServerAndClient。执行此操作后,您的操作过滤器应正确覆盖输出。

【讨论】:

  • 这样做会将 max-age 值添加到 Header,但也会设置 Expires: HTTP/1.1 200 OK Cache-Control: private, max-age=86400 Content-Type: text/html; charset=utf-8 过期时间:2013 年 2 月 12 日星期二 13:33:41 GMT
  • 我不能 100% 确定您所追求的最终结果,但我认为如果您在操作过滤器中添加对 Response.Cache.SetMaxAge 的调用,CC:max-age 和 Expires将匹配,你会很高兴去。
  • 我尝试设置 Response.Cache.SetMaxAge 并在 ServerAndClient 模式下被输出缓存覆盖。而只是在过滤模式下被忽略。我的目标是将 Cache-Control 设置为“public,max-age=10”并在 10 分钟后过期(格林威治标准时间)。 Cache-Control 由输出缓存控制,我似乎无法覆盖它。
  • 尝试将 OutputCache Location 设置为 Any,并在过滤器中使用 SetMaxAge(TimeSpan.FromMinutes(ExpireMinutes)),因为 max-age 以秒为单位。
  • 确实将标头正确设置为“Cache-Control: public, max-age=600”,但似乎服务器缓存的副本随后使用新的 max-age 而不是配置文件中的 86400 值,因为10 分钟后,浏览器会收到页面的新副本,而不是缓存中的副本。我猜这只是做不到。顺便说一句,这对 Google 来说很难。
【解决方案3】:

在 .Net 4.5 下,无需编写自定义缓存提供程序即可拥有不同的客户端和服务器缓存策略:

// Set the cache response expiration to 3600 seconds (use your own value here).
HttpContext.Current.Response.Cache.SetExpires(DateTime.UtcNow.AddSeconds(3600));

// Set both server and browser caching.
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.ServerAndPrivate);

// Prevent browser's default max-age=0 header on first request
// from invalidating the server cache on each request.
HttpContext.Current.Response.Cache.SetValidUntilExpires(true);

// Set an HTTP ETag header on the page using a random GUID.
HttpContext.Current.Response.Cache.SetETag(System.Guid.NewGuid()
                                           .ToString().Replace("-", ""));

// Set last modified time.
HttpContext.Current.Response.Cache.SetLastModified(DateTime.UtcNow);

// Now here is the critical piece that forces revalidation on each request!:
HttpContext.Current.Response.Cache.AppendCacheExtension(
    "must-revalidate, proxy-revalidate, max-age=0");

结果是每次在服务器缓存中重新生成页面时,它都会获得一个新的 ETag。这会导致 If-None-Match 请求将整个页面返回给浏览器。如果浏览器的缓存副本与服务器缓存中生成的相同(相同的 ETag),浏览器会返回 304 Not Modified。

请注意,我在 AppendCacheExtension 中附加的缓存标头都没有与本机缓存发出的标头冲突。每当您尝试修改 .Net 缓存本身发出的缓存标头时,.Net 将始终取代您尝试执行的操作。诀窍是添加新的非冲突标头,而不是尝试更改 .Net 已经发出的内容。最重要的是,您必须附加 max-age 标头。您不能使用 .SetMaxAge(),因为这也会设置服务器缓存的页面副本的最长期限。

这需要付出相当多的努力才能弄清楚,但它确实有效,至少在 .Net 4.5 中是这样。

【讨论】:

  • 这在技术上并不是设置不同的客户端缓存时间,最后一行我相信与设置 HttpContext.Current.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches); 没有什么不同。我赞成这个答案,因为使用这种解决方法比重新实现服务器端缓存要好得多。客户端针对服务器重新验证缓存的开销很小,所以我认为这是一个很好的答案。
猜你喜欢
  • 1970-01-01
  • 2014-12-02
  • 1970-01-01
  • 1970-01-01
  • 2017-05-30
  • 2011-09-06
  • 2011-08-07
  • 2011-01-12
  • 2015-11-04
相关资源
最近更新 更多