2010-01-21 12 views
15

Tengo una aplicación ASP.NET MVC. Sin embargo, debo almacenar en caché algunas páginas solo para usuarios no autenticados.Cómo desactivar el almacenamiento en caché de salida para usuarios autenticados en ASP.NET MVC?

He intentado utilizar VaryByCustom="user" con la aplicación GetVaryByCustomString siguiente:

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

Sin embargo, esto no es exactamente lo que necesito porque las páginas todavía están en caché. La única diferencia es que ahora se almacena en caché para cada usuario por separado.

Una posible solución es devolver Guid.NewGuid() cada vez que el usuario está autenticado, pero me parece una enorme pérdida de recursos.

¿Tienes algún consejo para mí?

Respuesta

32

Así que aquí es lo que he hecho:

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

Muchas gracias a Craig Stuntz que me apuntaban a corregir dirección y cuya respuesta que, sin saberlo, downvoted.

+1

¡Genial! Buena solución. –

+0

Interesante: ¿ha tenido algún problema con este método desde esta publicación (hace 1 año)? Gracias – UpTheCreek

+0

@UpTheCreek: Utilizamos una versión un poco más complicada de este código en nuestro producto. Obviamente, no garantizo nada, pero en mi experiencia funciona. –

12

Los atributos en general se almacenan en caché, luego debe almacenar la ubicación original. Si accede a la página Registrado, establece la Ubicación en Ninguno, luego cuando accede como anónimo, sigue siendo Ninguno.

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

Esta debería ser la respuesta aceptada. La respuesta de Jakub no funcionará en – Alex

+0

... pero aún necesita la parte "Ignorar autenticada" de la respuesta aceptada. – Alex

2

La respuesta aceptada es correcta, pero no funciona para el almacenamiento en caché de esta manera Vistas parciales. He combinado ambas variantes: GetVaryByCustomString y establezco Duration al mínimo - para vistas parciales y AddValidationCallback método para páginas. En realidad, es posible usar solo el primer método, pero el segundo no parece tan costoso; no llama al OnResultExecuting cada vez, sino solo al controlador registrado.

Así que el atributo de caché personalizada clase

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

método de reemplazo GetVaryByCustomString en 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); 
} 

utilizar de esta manera:

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

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

Actualizado: también tengo se agregó la solución de Fujiy aquí

Cuestiones relacionadas