2010-01-21 10 views
15

Ho un'applicazione ASP.NET MVC. Devo memorizzare alcune pagine tuttavia solo per utenti non autenticati.Come disattivare la cache di output per gli utenti autenticati in ASP.NET MVC?

Ho cercato di usare VaryByCustom="user" con l'implementazione GetVaryByCustomString seguente:

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

Tuttavia questo non è esattamente quello che mi serve perché le pagine sono ancora memorizzate nella cache. L'unica differenza è che ora viene memorizzato nella cache per ciascun utente separatamente.

Una possibile soluzione è quella di restituire Guid.NewGuid() ogni volta che l'utente viene autenticato, ma mi sembra un enorme spreco di risorse.

Quindi hai qualche consiglio per me?

risposta

32

ecco quello che ho fatto:

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

Molte grazie a Craig Stuntz che mi puntavano a correggere la direzione e la cui risposta che ho involontariamente downvoted.

+1

Fantastico! Bella soluzione –

+0

Interessante - hai avuto problemi con questo metodo da questo post (1 anno fa)? Grazie – UpTheCreek

+0

@UpTheCreek: Utilizziamo una versione leggermente più complicata di questo codice nel nostro prodotto. Ovviamente non garantisco nulla, ma nella mia esperienza funziona. –

12

Gli attributi in generale sono memorizzati nella cache, quindi è necessario memorizzare la posizione originale. Se accedi alla pagina Registrato, imposta Posizione su Nessuno, quindi quando accedi come anonimo, continua a Nessuno.

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

Questa dovrebbe essere la risposta accettata. La risposta di Jakub non funzionerà – Alex

+0

.. ma è ancora necessaria la parte "IgnoreAuthenticated" dalla risposta accettata – Alex

2

La risposta accettata è corretta ma non funziona per la memorizzazione nella cache in questo modo Visualizzazioni parziali. Ho combinato entrambe le varianti: GetVaryByCustomString e impostato al minimo Duration - per le viste parziali e il metodo AddValidationCallback per le pagine. In realtà è possibile utilizzare solo il primo metodo, ma il secondo non sembra così costoso - non chiama il numero OnResultExecuting ogni volta ma solo il gestore registrato.

Così l'attributo di cache personalizzato classe

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

metodo override GetVaryByCustomString 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); 
} 

Utilizzare in questo modo:

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

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

Aggiornamento: Ho anche ha aggiunto la correzione di Fujiy qui

Problemi correlati