【问题标题】:How to turn output caching off for authenticated users in ASP.NET MVC?如何在 ASP.NET MVC 中为经过身份验证的用户关闭输出缓存?
【发布时间】:2010-01-21 14:31:25
【问题描述】:

我有一个 ASP.NET MVC 应用程序。我需要为未经身份验证的用户缓存一些页面,但

我尝试将VaryByCustom="user" 与以下GetVaryByCustomString 实现一起使用:

public override string GetVaryByCustomString(HttpContext context, string custom)
{
  if (custom == "user")
  {
      if (context.User.Identity.IsAuthenticated)
      {
        return context.User.Identity.Name;
      }
      else
      {
        return "";
      }
  }  

  return base.GetVaryByCustomString(context, custom);
}

但这并不是我所需要的,因为页面仍然被缓存。唯一不同的是 now 是为每个用户单独缓存的。

一种可能的解决方案是每次用户通过身份验证时都返回Guid.NewGuid(),但这对我来说似乎是一种巨大的资源浪费。

那么你有什么建议给我吗?

【问题讨论】:

    标签: asp.net-mvc outputcache


    【解决方案1】:

    这就是我所做的:

    public class NonAuthenticatedOnlyCacheAttribute : OutputCacheAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
          var httpContext = filterContext.HttpContext;
    
          if (httpContext.User.Identity.IsAuthenticated)
          {
            // it's crucial not to cache Authenticated content
            Location = OutputCacheLocation.None;
          }
    
          // this smells a little but it works
          httpContext.Response.Cache.AddValidationCallback(IgnoreAuthenticated, null);
    
          base.OnResultExecuting(filterContext);
        }
    
        // This method is called each time when cached page is going to be
        // served and ensures that cache is ignored for authenticated users.
        private void IgnoreAuthenticated(HttpContext context, object data, ref HttpValidationStatus validationStatus)
        {
          if (context.User.Identity.IsAuthenticated)            
            validationStatus = HttpValidationStatus.IgnoreThisRequest;          
          else          
            validationStatus = HttpValidationStatus.Valid;          
        }
    }
    

    非常感谢Craig Stuntz,他为我指明了正确的方向,但我无意中否决了他的回答。

    【讨论】:

    • 有趣 - 自这篇文章(1 年前)以来,您对这种方法有任何问题吗?谢谢
    • @UpTheCreek:我们在产品中使用了稍微复杂一点的代码版本。显然,我不保证,但根据我的经验,它会起作用。
    • 它适用于页面,但不幸的是不适用于部分视图
    • -1。这行不通。一旦对经过身份验证的请求执行此逻辑,all 连续请求将没有缓存。请记住,该属性是在 AppDomain 的整个生命周期中存在的单个实例,跨越所有 HttpApplication 实例。要更改每个请求的 FilterAttribute 缓存行为,您必须重新实现 base.OnResultExecuting 中的逻辑,以向 Page.InitOutputCache 提供单独的实例。查看源代码。
    • gWiz 是对的——一旦你在 IsAuthenticated 检查中设置了位置,所有后续请求都不会被缓存。要解决此问题,请像这样缓存原始位置:stackoverflow.com/a/9694955/8037,然后将其用于未经身份验证的请求。
    【解决方案2】:

    属性一般都是缓存的,那么就需要存储原来的Location。如果您访问 Logged 页面,它将 Location 设置为 None,然后当您以匿名方式访问时,它仍然是 None。

    public class AuthenticatedOnServerCacheAttribute : OutputCacheAttribute
    {
        private OutputCacheLocation? originalLocation;
    
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            var httpContext = filterContext.HttpContext;
    
            if (httpContext.User.Identity.IsAuthenticated)
            {
                originalLocation = originalLocation ?? Location;
                Location = OutputCacheLocation.None;
            }
            else
            {
                Location = originalLocation ?? Location;
            }
    
            base.OnResultExecuting(filterContext);
        }
    }
    

    【讨论】:

    • ..但您仍然需要接受答案中的“IgnoreAuthenticated”部分
    【解决方案3】:

    接受的答案是正确的,但它不适用于以这种方式缓存部分视图。 我结合了两种变体: GetVaryByCustomString 并将 Duration 设置为最小值 - 用于部分视图和 AddValidationCallback 方法用于页面。实际上可以只使用第一种方法,但第二种方法看起来并不那么昂贵——它不会每次都调用OnResultExecuting,而只是注册处理程序。

    所以自定义缓存属性类

    public class CacheAttribute : OutputCacheAttribute
    {   
    
        public CacheAttribute()
        {
          Duration = 300;  /*default cache time*/
        }
    
        private bool _partialView;
    
        /// <summary>
        /// Set true if Partial view is cached
        /// </summary>
        public bool PartialView
        {
          get { return _partialView; }
          set
          {
            _partialView = value;
            if ( _partialView ) {
              VaryByCustom = "Auth";
            }
          }
        }
    
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            if ( PartialView ) OnCachePartialEnabled( filterContext );
            else OnCacheEnabled(filterContext);
    
            base.OnResultExecuting( filterContext );     
        }
    
        private OutputCacheLocation? originalLocation;
        private int? _prevDuration;
        protected void OnCachePartialEnabled(ResultExecutingContext filterContext)
        {
          var httpContext = filterContext.HttpContext;
    
          if ( !_prevDuration.HasValue) _prevDuration = Duration;
          Duration = httpContext.User.Identity.IsAuthenticated ? 1 : _prevDuration.Value;
        }
    
        protected void OnCacheEnabled(ResultExecutingContext filterContext)
        {
          var httpContext = filterContext.HttpContext;
    
          if ( httpContext.User.Identity.IsAuthenticated ) {
            // it's crucial not to cache Authenticated content
            originalLocation = originalLocation ?? Location;
            Location = OutputCacheLocation.None;
          }
          else {
          Location = originalLocation ?? Location;
        }
    
          // this smells a little but it works
          httpContext.Response.Cache.AddValidationCallback( IgnoreAuthenticated, null );      
        }
    
        // This method is called each time when cached page is going to be
        // served and ensures that cache is ignored for authenticated users.
        private void IgnoreAuthenticated(HttpContext context, object data, ref HttpValidationStatus validationStatus)
        {
          validationStatus = context.User.Identity.IsAuthenticated 
            ? HttpValidationStatus.IgnoreThisRequest 
            : HttpValidationStatus.Valid;
        }
    }
    

    覆盖 Global.asax.cs 中的 GetVaryByCustomString 方法

    public override string GetVaryByCustomString(HttpContext context, string custom)
    { 
        if ( custom == "Auth" ) {
          //do not cache when user is authenticated
          if ( context.User.Identity.IsAuthenticated ) {
            return base.GetVaryByCustomString( context, custom );
          }
          return "NotAuth";
        }     
        return base.GetVaryByCustomString( context, custom );
    }
    

    像这样使用它:

    [Cache]
    public virtual ActionResult Index()
    {
        return PartialView();
    }
    
    [ChildActionOnly, Cache(PartialView=true)]
    public virtual ActionResult IndexPartial()
    {
        return PartialView();
    }
    

    更新:我还在这里添加了 Fujiy 的修复程序

    【讨论】:

      猜你喜欢
      • 2012-02-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-10
      • 2015-06-21
      • 1970-01-01
      • 2015-04-20
      相关资源
      最近更新 更多