防止使用属性在ASP.NET MVC中缓存特定操作


196

我有一个ASP.NET MVC 3应用程序。该应用程序通过jQuery请求记录。jQuery回调到控制器操作,该操作以JSON格式返回结果。我无法证明这一点,但是我担心我的数据可能会被缓存。

我只希望将缓存应用于特定的操作,而不是应用于所有操作。

是否可以执行一项操作以确保不缓存数据的属性?如果没有,如何确保浏览器每次都获得一组新的记录,而不是一组缓存的记录?


1
如果您怀疑正在缓存某些内容,那么我建议您在这里阅读缓存控制机制:w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9

Answers:


304

为了确保JQuery不会缓存结果,请在您的ajax方法上放置以下内容:

$.ajax({
    cache: false
    //rest of your ajax setup
});

为了防止在MVC中进行缓存,我们创建了自己的属性,您可以这样做。这是我们的代码:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class NoCacheAttribute : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        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();

        base.OnResultExecuting(filterContext);
    }
}

然后只需使用来装饰您的控制器[NoCache]。或为所有目的做到这一点,您只需将属性放在继承您的控制器(如果有)的基类的类上,就像我们在这里一样:

[NoCache]
public class ControllerBase : Controller, IControllerBase

如果您需要一些不可缓存的动作,也可以用此属性来装饰某些动作,而不是装饰整个控制器。

如果您的类或动作NoCache在浏览器中呈现时没有,并且您想检查它是否正常工作,请记住,编译更改后,需要在浏览器中执行“硬刷新”(Ctrl + F5)。在执行此操作之前,浏览器将保留旧的缓存版本,并且不会使用“常规刷新”(F5)对其进行刷新。


1
我尝试了上述解决方案中的所有内容,但对我不起作用。
欧比

9
根据我的理解(并且我不是jQuery专家),cache:false仅使jQuery固定在查询字符串上的值会发生变化,从而“欺骗”浏览器以为请求是针对其他请求的。从理论上讲,这意味着浏览器仍然会缓存结果,只是不会使用缓存的结果。在客户端上应该更有效地通过响应头禁用缓存。
乔什(Josh)2013年

2
仅在控制器级别而不是操作级别上工作。
拉梅什2014年

3
我会给予好评,包括在官方的ASP.NET包:-)这样的属性
USR-本地ΕΨΗΕΛΩΝ

1
@Frédéric,您指向的规范部分说,缓存不能缓存非存储内容:“ no-store”响应指令指示高速缓存绝不能存储立即请求或响应的任何部分。
kristianp

258

您可以使用内置的缓存属性来防止缓存。

对于.net Framework: [OutputCache(NoStore = true, Duration = 0)]

对于.net Core: [ResponseCache(NoStore = true, Duration = 0)]

请注意,不可能强制浏览器禁用缓存。您能做的最好的事情就是提供大多数浏览器都会推荐的建议,通常是标题或元标记的形式。此decorator属性将禁用服务器缓存,并添加以下标头:Cache-Control: public, no-store, max-age=0。它不添加元标记。如果需要,可以将其手动添加到视图中。

另外,JQuery和其他客户端框架将尝试通过向URL中添加内容(例如时间戳或GUID)来欺骗浏览器以不使用其资源的缓存版本。这可以有效地使浏览器再次请求资源,但实际上并不能阻止缓存。

最后一点。您应该意识到,资源也可以缓存在服务器和客户端之间。ISP,代理和其他网络设备也缓存资源,并且它们通常使用内部规则而不查看实际资源。您无能为力。好消息是,它们通常缓存较短的时间范围,例如秒或分钟。


3
我相信这不能完全解决这个问题。这将禁用ASP.NET缓存,但不会禁用浏览器缓存。
Rosdi Kasim 2014年

22
不可能强制浏览器禁用缓存。您能做的最好的事情就是提供大多数浏览器都会推荐的建议,通常是标题或元标记的形式。此decorator属性将禁用.NET服务器缓存,并添加标头Cache-Control:public, no-store, max-age=0。它不添加元标记。如果需要,可以将其手动添加到视图中。
Jaguir 2014年

1
我能理解您为什么要使用NoStore = trueand Duration = 0(我已经成功使用了,谢谢),但是VaryByParam = "None"其他两个选项会影响所有请求,而与参数无关,还会有什么其他效果?
Gone Coding 2015年

我认为MVC不需要它,只是我很明确。我确实记得在ASP.NET Web表单和用户控件中,此属性或VaryByControl属性都是必需的。
Jaguir 2015年

1
对于ASP.NET Core,请使用:'[[ResponseCache(NoStore = true,Duration = 0)]'
Jeff

48

所有你需要的是:

[OutputCache(Duration=0)]
public JsonResult MyAction(

或者,如果您想为整个控制器禁用它:

[OutputCache(Duration=0)]
public class MyController

尽管此处有评论争论,但这足以禁用浏览器缓存-这导致ASP.Net发出响应头,告知浏览器文档立即过期:

OutputCache持续时间= 0响应标头:max-age = 0,s-maxage = 0


6
当仅在Controller Action上使用Duration = 0来单击后退按钮时,IE8仍将呈现页面的缓存版本。使用NoStore = true以及Duration = 0(请参见Jared的答案)解决了我的问题。
基思·凯特勒

3
设置Cache-Controlpublic
ta.speot.is

max-age=0从不意味着“禁用缓存”。这仅意味着将响应内容视为立即过期,但是允许使用缓存对其进行缓存。浏览器应在使用之前验证缓存的过时内容的新鲜度,但这不是强制性的,除非must-revalidate指定了其他指令。
弗雷德里克

14

在控制器操作中,将以下行添加到标题

    public ActionResult Create(string PositionID)
    {
        Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
        Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
        Response.AppendHeader("Expires", "0"); // Proxies.

5

这是NoCachemattytommo提出的属性,通过使用Chris Moschini的答案中的信息进行了简化:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class NoCacheAttribute : OutputCacheAttribute
{
    public NoCacheAttribute()
    {
        this.Duration = 0;
    }
}

由于某些原因,MVC 3不仅允许您将持续时间设置为0。您还必须添加这些注释...感谢您的解决方法!
micahhoover 2015年

max-age=0从不意味着“禁用缓存”。这仅意味着将响应内容视为立即过期,但是允许使用缓存对其进行缓存。浏览器应在使用之前验证缓存的过时内容的新鲜度,但这不是强制性的,除非must-revalidate指定了其他指令。
弗雷德里克

为了完整起见,最小且更合适的指令是no-cache,该指令仍允许缓存,但必须在使用前在原始服务器上重新验证。为了避免甚至重新验证缓存,你必须添加no-store一起no-cache。(no-store仅是错误的做法,因为易失性缓存允许缓存标记为的内容no-store。)
Frédéric15年

4

对于MVC6(DNX),没有System.Web.OutputCacheAttribute

注意:设置NoStore持续时间参数时不考虑。可以设置首次注册的初始时长,并使用自定义属性覆盖此初始时长。

但是我们有 Microsoft.AspNet.Mvc.Filters.ResponseCacheFilter

 public void ConfigureServices(IServiceCollection services)
        ...
        services.AddMvc(config=>
        {
            config.Filters.Add(
                 new ResponseCacheFilter(
                    new CacheProfile() { 
                      NoStore=true
                     }));
        }
        ...
       )

可以使用自定义属性覆盖初始过滤器

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public sealed class NoCacheAttribute : ActionFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            var filter=filterContext.Filters.Where(t => t.GetType() == typeof(ResponseCacheFilter)).FirstOrDefault();
            if (filter != null)
            {
                ResponseCacheFilter f = (ResponseCacheFilter)filter;
                f.NoStore = true;
                //f.Duration = 0;
            }

            base.OnResultExecuting(filterContext);
        }
    }

这是一个用例

    [NoCache]
    [HttpGet]
    public JsonResult Get()
    {            
        return Json(new DateTime());
    }

1

MVC中的输出缓存

[OutputCache(NoStore = true,Duration = 0,Location =“ None”,VaryByParam =“ *”)]

要么
[OutputCache(NoStore = true,Duration = 0,VaryByParam =“ None”)]


查看其它评论(123上众多的答案用这个已经暗示)。您的第二行是错误的,将导致某些浏览器出现问题。
弗雷德里克


0

ASP.NET MVC 5解决方案:

  1. 在中心位置缓存预防代码:App_Start/FilterConfig.csRegisterGlobalFilters方法:
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            // ...
            filters.Add(new OutputCacheAttribute
            {
                NoStore = true,
                Duration = 0,
                VaryByParam = "*",
                Location = System.Web.UI.OutputCacheLocation.None
            });
        }
    }
  1. 一旦有了适当的设置,我的理解就是可以通过OutputCache在级别ControllerView级别应用其他指令来覆盖全局过滤器。对于常规控制器,
[OutputCache(NoStore = true, Duration = 0, Location=System.Web.UI.ResponseCacheLocation.None, VaryByParam = "*")]

或如果它ApiController

[System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, Location = System.Web.UI.OutputCacheLocation.None, VaryByParam = "*")]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.