简体   繁体   English

如何在ASP.NET MVC中为经过身份验证的用户关闭输出缓存?

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

I have an ASP.NET MVC application. 我有一个ASP.NET MVC应用程序。 I need to cache some pages however only for non-authenticated users. 我需要缓存一些页面,但仅限于未经过身份验证的用户。

I've tried to use VaryByCustom="user" with the following GetVaryByCustomString implementation: 我尝试使用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);
}

However this isn't exactly what I need because pages are still cached. 然而,这并不是我需要的,因为页面仍然被缓存。 Only difference is that now is cached for each user separately. 唯一的区别是现在分别为每个用户缓存。

One possible solution is to return Guid.NewGuid() each time when user is Authenticated, but it looks like a huge waste of resources to me. 一种可能的解决方案是每次用户进行身份验证时返回Guid.NewGuid() ,但这对我来说似乎是一种巨大的资源浪费。

So do you have any tips for me? 所以你有什么提示吗?

So here is what I done: 所以这就是我所做的:

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

Many thanks to Craig Stuntz who pointed me to correct direction and whose answer I unwittingly downvoted. 非常感谢Craig Stuntz向我指出了正确的方向,我的答案在不知不觉中被低估了。

Attributes in general are cached, then you need to store original Location. 通常会对属性进行缓存,然后您需要存储原始位置。 If you access the page Logged, it set Location to None, then when you access as anonymous, it still None. 如果您访问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);
    }
}

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. 我结合了两种变体: GetVaryByCustomString并将Duration设置为最小 - 对于页面的部分视图和AddValidationCallback方法。 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. 实际上可以只使用第一种方法,但第二种方法看起来不那么昂贵 - 它不会每次调用OnResultExecuting而只调用注册处理程序。

So the custom cache attribute class 所以自定义缓存属性类

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

Override GetVaryByCustomString method in Global.asax.cs 覆盖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 );
}

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 更新:我还在这里添加了Fujiy的修复程序

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM