在IIS7 + ASP.NET MVC中未将Cache-control: no-store, must-revalidate发送到客户端浏览器。

33

我想确保某个页面永远不被缓存,并且当用户点击“返回”按钮时永远不会显示。 这个非常高评价的答案(目前有1068个赞)建议使用:

Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache");
Response.AppendHeader("Expires", "0");

然而在 IIS7 / ASP.NET MVC 中,当我发送这些头部时,客户端看到的是这些响应头部:

Cache-control: private, s-maxage=0 // that's not what I set them to
Pragma: no-cache
Expires: 0

缓存控制头发生了什么事?IIS7或ASP.NET本身会覆盖它吗?我检查了我的解决方案,没有代码会覆盖此标头。

当我首先添加Response.Headers.Remove("Cache-Control");,它没有任何影响:

Response.Headers.Remove("Cache-Control");
Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache");
Response.AppendHeader("Expires", "0");

当我添加一个 [OutputCache] 属性时:

[OutputCache(Location = OutputCacheLocation.None)]
public ActionResult DoSomething()
{
   Response.Headers.Remove("Cache-Control");
   Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
   Response.AppendHeader("Pragma", "no-cache");
   Response.AppendHeader("Expires", "0");

   var model = DoSomething();
   return View(model);
}

然后客户端响应头发生变化:

Cache-control: no-cache
Pragma: no-cache
Expires: 0

哪些头部信息更接近我想发送的,但仍不是我想要发送的头部信息。这些头部信息被覆盖在哪里,我该如何阻止它?

编辑:我已经检查过,错误的头部信息被发送到Chrome、FF、IE和Safari,因此看起来是一个服务器问题而不是一个浏览器相关的问题。


我无法在全新的 MVC3 或 MVC4 应用程序中复制此问题。你能检查一下你在 IIS 中的设置(HTTP 响应标头输出缓存)吗? - Rowan Freeman
1
在IIS7中,我没有配置任何输出缓存设置(服务器级别或站点级别),只配置了一个响应头(X-Powered-By)。 - JK.
3个回答

56

通过反复试错,我发现在ASP.NET MVC中为IIS7正确设置头文件的一种方法是:

Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.AppendCacheExtension("no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache");
Response.AppendHeader("Expires", "0");

第一行将Cache-control设置为no-cache,第二行添加了其他属性no-store, must-revalidate

这可能并不是唯一的方法,但如果更直接的方法Response.AppendHeader("Cache-control", "no-cache, no-store, must-revalidate");失败,它提供了一种替代方法。

其他相关的IIS7缓存控制问题可以通过此方法解决:


5
我喜欢MVC和IIS在这里和那里自己决定随机中断它们自己的API并引入静默故障。 - A.R.
也适用于IIS 8,这个答案也是如此:https://dev59.com/hmTWa4cB1Zd3GeqPFaVj#33065747 - Whelkaholism
1
这是唯一对我有效的方法,即使在Windows 10上的IIS 10上也是如此。谢谢!其他设置“无缓存”头的答案没有导致输出“no-store”值,这会导致浏览器缓存某些内容并在用户使用后退/前进浏览器按钮时加载它。 - mfranchi
我通过Global.asax.cs和控制器动作过滤器强制IIS将响应头设置为"private, no-store, max-age=0",但是IIS显示为"public, no-store, max-age=0"。最终这种方法在IIS 10中也适用于我。非常有帮助! - quasar
no-store和no-cache是互斥的。实际上,no-cache告诉浏览器它绝对应该缓存,并且它优先于no-store,使得no-store成为一个无操作。你唯一需要设置的Cache-Control是no-store,而不是其他任何值。 - undefined

1
如果您希望在MVC应用程序中全局使用这些标头,请添加此类。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class CustomHeaders : System.Web.Mvc.ActionFilterAttribute
{
    [OutputCache(Location = System.Web.UI.OutputCacheLocation.None)]
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        context.RequestContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        context.RequestContext.HttpContext.Response.Cache.AppendCacheExtension("no-store, must-revalidate");
        context.RequestContext.HttpContext.Response.AppendHeader("Pragma", "no-cache");
        context.RequestContext.HttpContext.Response.AppendHeader("Expires", "0");

        base.OnActionExecuted(context);
    }
}

将其添加到FilterConfig以进行全局使用。

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new CustomHeaders());
    }
}

或者只在特定的控制器上使用这些标题。

[Authorize]
[CustomHeaders]
public class HomeController : Controller
{
    [AllowAnonymous]
    public ActionResult Index()

顺便提一下:您可以使用IIS和web.config来处理其他头信息。例如,对于静态内容(如jquery、bootstrap),请查找这些部分:customheaders、staticcontent。


1

我想在JK的回答中添加一些内容:
如果您将缓存控制设置为比现有值更严格的值,那么这是可以的。(例如:当它是私有时,设置为无缓存)

但是,如果您想将其设置为比现有值更不严格的值(例如:当它是无缓存时,将其设置为私有),则下面的代码将无法正常工作:

Response.Cache.SetCacheability(HttpCacheability.Private);

因为SetCacheablitiy方法具有以下代码,并且仅在缓存标志更加严格时设置缓存标志:
if (s_cacheabilityValues[(int)cacheability] < s_cacheabilityValues[(int)_cacheability]) {
    Dirtied();
   _cacheability = cacheability;
}

为了在 .net mvc 中解决这个问题,您需要获取一个 HttpResponseMessage 实例,并将 CacheControlHeaderValue 赋值给其 Headers.CacheControl 属性:

actionExecutedContext.Response.Headers.CacheControl = new CacheControlHeaderValue
                                   {
                                       MaxAge = TimeSpan.FromSeconds(3600),
                                       Private = true
                                   };

HttpResponseMessage 的实例可在操作筛选器中使用。您可以编写一个操作筛选器来设置缓存标头值,如下所示:

public class ClientSideCacheAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        var response = actionExecutedContext.ActionContext.Response;
        response.Headers.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue
        {
            MaxAge = TimeSpan.FromSeconds(9999),
            Private = true,
        };
    }
}

ActionFilterAttribute 中的 OnActionRequested 似乎需要一个 ActionExecutedContext 而不是 HttpActionExecutedContext。ActionExecutedContext 不允许访问 ActionContex(仅限于 HttpContext.Response.Headers,其中没有 CacheControl),有何建议?(注意 MVC 而不是 WebAPI) - Andiih

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接