Как отключить кэширование вывода для аутентифицированных пользователей в ASP.NET MVC? - PullRequest
15 голосов
/ 21 января 2010

У меня есть приложение 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);
}

Однако это не совсем то, что мне нужно, потому что страницы все еще кэшируются. Разница лишь в том, что теперь кешируется для каждого пользователя отдельно.

Одно из возможных решений - возвращать Guid.NewGuid() каждый раз, когда пользователь проходит проверку подлинности, но для меня это выглядит как огромная трата ресурсов.

Так у вас есть какие-нибудь советы для меня?

Ответы [ 3 ]

32 голосов
/ 22 января 2010

Итак, вот что я сделал:

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

Огромное спасибо Крейгу Штунцу , который указал мне верное направление и за чей ответ я невольно отказался.

12 голосов
/ 14 марта 2012

Атрибуты вообще кешируются, тогда вам нужно сохранить исходное местоположение. Если вы открываете страницу «Зарегистрированные», для нее устанавливается «Нет», а при доступе как анонимной - «Нет».

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);
    }
}
2 голосов
/ 25 апреля 2012

Принятый ответ правильный, но он не работает для кэширования таким образом. Частичные представления. Я объединил оба варианта: 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;
    }
}

Переопределить метод GetVaryByCustomString в 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 );
}

Используйте это так:

[Cache]
public virtual ActionResult Index()
{
    return PartialView();
}

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

Обновлено: я также добавил исправление Фуджи здесь

...