如何为客户端和服务器缓存设置不同的缓存过期时间

时间:2013-02-11 23:44:17

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

我希望某些页面有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标头显示:

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应用程序。

3 个答案:

答案 0 :(得分:8)

您需要为服务器端缓存和客户端缓存实现自己的解决方案,使用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 进行客户端缓存,并为服务器端缓存实现自己的缓存机制。

答案 1 :(得分:4)

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

答案 2 :(得分: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中。