web-dev-qa-db-pt.com

Cache-control: no-store, must-revalidate não enviado ao navegador do cliente no IIS7 + ASP.NET MVC

Eu estou tentando certificar-se de que uma determinada página nunca é armazenada em cache e nunca mostrada quando o usuário clica no botão Voltar. Esta resposta muito bem avaliada (atualmente 1068 upvotes) diz para usar :

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

No entanto, no IIS7/ASP.NET MVC, quando envio esses cabeçalhos, o cliente vê esses cabeçalhos de resposta:

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

O que aconteceu com o cabeçalho de controle de cache? Alguma coisa nativa do IIS7 ou do ASP.NET sobrescreve? Verifiquei minha solução e não tenho código que substitua esse cabeçalho.

Quando adiciono Response.Headers.Remove("Cache-Control"); primeiro, não faz diferença:

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

Quando eu adiciono um atributo [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);
}

Em seguida, os cabeçalhos de resposta do cliente mudam para:

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

Qual é o mais próximo, mas ainda não os cabeçalhos que eu quero enviar. Onde esses cabeçalhos são substituídos e como posso pará-lo?

EDIT: eu verifiquei e os cabeçalhos incorretos estão sendo enviados para o Chrome, FF, IE e Safari, por isso parece ser um problema de servidor não um problema relacionado ao navegador.

23
JK.

Por tentativa e erro, descobri que uma maneira de definir os cabeçalhos corretamente para o IIS7 no ASP.NET MVC é:

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

A primeira linha define Cache-control para no-cache e a segunda linha adiciona os outros atributos no-store, must-revalidate.

Este pode não ser o único caminho, mas fornece um método alternativo se o Response.AppendHeader("Cache-control", "no-cache, no-store, must-revalidate"); mais simples falhar.

Outras questões de controle de cache do IIS7 relacionadas que podem ser resolvidas com isso são:

36
JK.

Se você precisar desses cabeçalhos globalmente em seu aplicativo MVC. Adicione esta classe.

[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);
    }
}

Para uso global, adicione-o ao FilterConfig.

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

Ou use apenas esses cabeçalhos em um controlador específico.

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

Nota: você pode usar o IIS e web.config para outros cabeçalhos. Por exemplo, em conteúdo estático, como seus pacotes (jquery, bootstrap). Procure por estas seções customheaders, staticcontent.

0
ivw

Eu quero adicionar algo à resposta de JK :
Se você estiver configurando o controle de cache para um valor mais restritivo do que já é, tudo bem. (isto é, sem cache, quando é privado) 

Mas, se você quiser definir um valor menos restritivo do que já é (isto é, definindo como privado, quando não for cache), o código abaixo não funcionará: 

Response.Cache.SetCacheability(HttpCacheability.Private);

Porque, o método SetCacheablitiy tem esse código abaixo e define o sinalizador de cache somente se for mais restritivo: 

if (s_cacheabilityValues[(int)cacheability] < s_cacheabilityValues[(int)_cacheability]) {
    Dirtied();
   _cacheability = cacheability;
}

Para superar isso no .net mvc, você precisa obter uma instância de HttpResponseMessage e atribuir uma CacheControlHeaderValue ao seu valor Headers.CacheControl

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

Uma instância da HttpResponseMessage está disponível em filtros de ação. Você pode escrever um filtro de ação para definir valores de cabeçalho de cache como este: 

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,
        };
    }
}
0
Veysel Özdemir