How to turn output caching off for authenticated users in ASP.NET MVC?

后端 未结 3 1377
余生分开走
余生分开走 2020-12-05 01:11

I have an ASP.NET MVC application. I need to cache some pages however only for non-authenticated users.

I\'ve tried to use VaryByCustom=\"user

3条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-05 01:47

    The accepted answer is correct but it doesn't work for caching in this way Partial views. I have combined both variants: GetVaryByCustomString and set Duration to the minimum - for Partial Views and AddValidationCallback method for pages. Actually it is possible to use only the first method but the second one looks not such expensive - it doesn't call OnResultExecuting each time but only registered handler.

    So the custom cache attribute class

    public class CacheAttribute : OutputCacheAttribute
    {   
    
        public CacheAttribute()
        {
          Duration = 300;  /*default cache time*/
        }
    
        private bool _partialView;
    
        /// 
        /// Set true if Partial view is cached
        /// 
        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;
        }
    }
    

    Override GetVaryByCustomString method in Global.asax.cs

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

    Use it like this:

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

    Updated: I have also added Fujiy's fix here

提交回复
热议问题