禁用整个ASP.NET网站的浏览器缓存

204

我正在寻找一种方法来禁用整个ASP.NET MVC网站的浏览器缓存。

我发现了以下方法:

Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
Response.Cache.SetNoStore();

还有一种元标记的方法(对于我来说不起作用,因为一些MVC操作通过Ajax发送部分HTML / JSON,没有head、meta标记)。

<meta http-equiv="PRAGMA" content="NO-CACHE">

但我正在寻找一种简单的方法来禁用整个网站的浏览器缓存。


3
可能是 如何在 ASP.NET MVC 中禁用客户端和代理缓存? 的重复问题。 - Robert MacLean
8个回答

369
创建一个继承自IActionFilter的类。
public 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]
[HandleError]
public class AccountController : Controller
{
    [NoCache]
    [Authorize]
    public ActionResult ChangePassword()
    {
        return View();
    }
}

19
与其使用HttpContext.Current.Response,你应该使用filterContext.HttpContext.Response,因为HttpContext.Current返回的是旧版的HttpContext对象,而filterContext.HttpContext返回的是新版本的HttpContextBase对象。这样可以提高可测试性和一致性。 - mkedobbs
5
IActionFilter已经在ActionFilterAttribute上实现,因此您不需要重复它。 - Andrew Davey
108
在当前的ASP.NET MVC版本中,你可以简单地使用OutputCacheAttribute来防止缓存:[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")] - Ashley Tate
9
我想指出,我刚刚花了几天时间尝试了所有的ASP.NET MVC缓存解决方案,包括这个问题的被接受的答案,但都没有成功。而这个答案——使用属性——起作用了。如果可以的话,我希望能够为它点赞一百万... - Tom Kidd
5
在代码的顶部添加 if (filterContext.IsChildAction) return;,可以防止外部操作在调用一个被 NoCache 属性修饰的子操作时也被标记为“不缓存”。换句话说,如果其他操作执行了子操作,NoCache 属性就不会泄漏到它们身上。此外,类名应该改为 NoCacheAttribute,以符合属性的通用命名约定。 - Jakub Konecki
显示剩余13条评论

132

不要自己重新发明轮子,可以直接使用提供给你的工具。

正如之前提到的那样,不要关闭所有缓存。例如,在 ASP.NET MVC 中广泛使用的 jQuery 脚本应该被缓存。实际上最好使用CDN来处理这些内容,但我的意思是有些内容应该被缓存。

我发现在这里最好的方法不是到处添加 [OutputCache],而是使用一个类:

[System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public class NoCacheController  : Controller
{
}

你想要禁用缓存的所有控制器都要继承自这个控制器。

如果你需要覆盖NoCacheController类中的默认设置,只需在你的操作方法上指定缓存设置,操作方法上的设置将优先生效。

[HttpGet]
[OutputCache(NoStore = true, Duration = 60, VaryByParam = "*")]
public ViewResult Index()
{
  ...
}

4
@Ozziepeeps,您的评论是不正确的。MSDN文档讨论了浏览器缓存,而一个简单的测试将显示此属性将更改Cache-Control响应头为“Cache-Control:public,no-store,max-age = 0”,而无需使用该属性,“Cache-Control:private”。 - Adam Tuliper
2
另外,你可以使用这个属性来控制所有三个位置(服务器、代理、客户端),所以绝对可以控制超出服务器缓存的内容。请参阅http://www.asp.net/mvc/tutorials/improving-performance-with-output-caching-cs获取更多详细信息。 - Adam Tuliper
2
请注意,如果您在类级别使用 [System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")],则无法在该类中使用PartialViews。 - Vivian River
1
OutputCache方法在两个条件同时存在时无法阻止IE缓存:1)操作不带参数,2)操作仅通过Content(someText)返回文本。当我返回JSON并带有参数时,IE缓存被正确地打败了。 - Kasey Speakman
1
@DanielAllenLangdon和Chris Stephens - 这个方法为什么不能用于部分视图? - Daniel Schilling
显示剩余7条评论

94
HttpContext.Current.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
HttpContext.Current.Response.Cache.SetValidUntilExpires(false);
HttpContext.Current.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.Cache.SetNoStore();

所有请求都会首先经过 default.aspx 页面,所以假设您可以直接在其代码后面添加代码。


17
我会把它放到Global.asax.cs的Application_BeginRequest()中。我不信任这个default.aspx的东西...另一个问题:这是否比[OutputCache]属性具有优先权? - chris166
5
我喜欢这个简单的想法,即创建一个全局操作过滤器,并以这种方式处理这些内容。 这样就不需要担心Default.aspx和Global.asax了。 - Keith Adler
13
将这段代码放在Application_BeingRequest中可能会引起一些问题。如果您的图片通过 .net 运行时路由(如果您使用通配符映射来生成美观的URL,则可能会发生这种情况),那么浏览器将不会缓存任何图像。这会导致页面加载时间变得非常慢,因为每个页面请求都需要重新下载所有图像。 - herbrandson
4
以编程方式使用任何内容都会覆盖已声明的属性。换句话说,使用 OP 的代码将覆盖任何已声明的 [OutputCache] 属性。 - Dave Black
@herbrandson,如果不是在Application_BeginRequest()中,你认为这段代码应该放在哪里呢? - ᴍᴀᴛᴛ ʙᴀᴋᴇʀ
显示剩余3条评论

10

您可能希望禁用控制器渲染的所有页面(即HTML页面)的浏览器缓存,但保留资源(例如脚本、样式表和图像)的缓存。如果您正在使用MVC4+捆绑和缩小功能,则需要保留脚本和样式表的默认缓存持续时间(非常长的持续时间,因为缓存是基于唯一URL的更改而无效,而不是基于时间的)。

在MVC4+中,要禁用所有控制器的浏览器缓存,但保留未由控制器提供的任何内容的缓存,请将以下内容添加到FilterConfig.RegisterGlobalFilters

filters.Add(new DisableCache());

DisableCache定义如下:

class DisableCache : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    }
}

不幸的是,这似乎不起作用,因为在注销后点击返回按钮会显示页面。 - ᴍᴀᴛᴛ ʙᴀᴋᴇʀ

6

我知道这个答案并不完全涉及到问题,但它可能会对某些人有用。

如果您想临时禁用整个ASP.NET MVC网站的浏览器缓存,那么最好是在浏览器中禁用缓存,而不是更改网站代码。

这里是Chrome浏览器的截图


这正是我一直在寻找的……在开发过程中,如果我更改了一个 .js 文件,当我需要进行小的故障排除/刷新/测试循环时,让它立即生效是非常困难的。这太完美了,谢谢!这让我的客户端调试工作变得更加轻松。 - jleach

2

我实施了所有之前的答案,但仍有一个视图无法正常工作。

原来我遇到问题的视图名称为“最近”(Recent)。显然这会让Internet Explorer浏览器感到困惑。

在我将视图名称(在控制器中)更改为另一个名称(我选择了“Recent5”)后,上述解决方案开始起作用。


0
你可以在 Global.asax 文件中尝试以下代码。
protected void Application_BeginRequest()
    {
        Response.Cache.SetCacheability(HttpCacheability.NoCache);
        Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
        Response.Cache.SetNoStore();
    }

-1

UI

<%@ OutPutCache Location="None"%>
<%
    Response.Buffer = true;
    Response.Expires = -1;
    Response.ExpiresAbsolute = System.DateTime.Now.AddSeconds(-1);
    Response.CacheControl = "no-cache";
%>

背景

Context.Response.Cache.SetCacheability(HttpCacheability.NoCache); 
Response.Expires = -1;          
Response.Cache.SetNoStore();

2
需要解释性的散文,即使它在技术上是正确的。 - rmalayter

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