Utilizando Microsoft.Web.DistributedCache.DistributedCacheOutputCacheProvider
de Windows Azure como proveedor de salida de cache para una aplicación MVC3. Este es el método de acción relevante:¿Por qué no puedo combinar los atributos [Authorize] y [OutputCache] cuando uso la memoria caché de Azure (aplicación .NET MVC3)?
[ActionName("sample-cached-page")]
[OutputCache(Duration = 300, VaryByCustom = "User",
Location = OutputCacheLocation.Server)]
[Authorize(Users = "[email protected],[email protected]")]
public virtual ActionResult SampleCachedPage()
{
return View();
}
consigo la siguiente excepción al cargar esta vista desde un navegador web:
System.Configuration.Provider.ProviderException: When using a custom output cache provider like 'DistributedCache', only the following expiration policies and cache features are supported: file dependencies, absolute expirations, static validation callbacks and static substitution callbacks.
System.Configuration.Provider.ProviderException: When using a custom output cache provider like 'DistributedCache', only the following expiration policies and cache features are supported: file dependencies, absolute expirations, static validation callbacks and static substitution callbacks.
at System.Web.Caching.OutputCache.InsertResponse(String cachedVaryKey, CachedVary cachedVary, String rawResponseKey, CachedRawResponse rawResponse, CacheDependency dependencies, DateTime absExp, TimeSpan slidingExp)
at System.Web.Caching.OutputCacheModule.OnLeave(Object source, EventArgs eventArgs)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Si quito el atributo [Autorizar], la vista cachés como sería esperado. ¿Esto significa que no puedo poner [OutputCache] en un método de acción que debe tener [Authorize]? O bien, ¿debo anular AuthorizeAttribute con una implementación personalizada que utiliza un método de devolución de llamada de validación estática para el caché?
Actualización 1
Después de la respuesta de Evan, he probado el método de acción anteriormente en IIS Express (fuera de Azure). Aquí está mi anulación de la propiedad = VaryByCustom "usuario" en el atributo OutputCache:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
return "User".Equals(custom, StringComparison.OrdinalIgnoreCase)
? Thread.CurrentPrincipal.Identity.Name
: base.GetVaryByCustomString(context, custom);
}
Cuando visito la página de muestra en caché como [email protected], se almacena en caché la salida de la página, y la vista pantallas "Esta página fue almacenada en caché el 12/31/2011 11:06: AM (UTC)". Si luego cierro la sesión e inicio sesión como [email protected] y visito la página, aparece "Esta página fue almacenada en caché al 31/12/2011 11:06: AM (UTC)". Al volver a iniciar sesión como [email protected] y al volver a visitar la página, se muestra la caché "Esta página se almacenó en caché el 31/12/2011 11:06: AM (UTC)" nuevamente. Otros intentos de inicio/finalización de sesión muestran que se está almacenando en la memoria caché diferente & según el usuario.
Esto me lleva a creer que la salida se almacena en caché por separado en función del usuario, que es la intención con mi configuración VaryByCustom = "Usuario" & anular. El problema es que no funciona con el proveedor de caché distribuida de Azure. Evan, ¿respondes que solo el contenido público en caché sigue en pie?
Actualización 2
Desenterré la fuente, y se encontró que el AuthorizeAttribute fuera de la caja tiene de hecho una devolución de llamada de validación no estático. He aquí un extracto de OnAuthorization
:
if (AuthorizeCore(filterContext.HttpContext)) {
// ** IMPORTANT **
// Since we're performing authorization at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether a page should be served from the cache.
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
}
else {
HandleUnauthorizedRequest(filterContext);
}
CacheValidationHandler
delegados a la validación caché protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase)
, que por supuesto no es estática. Una razón por la cual no es estática es porque, como se menciona en el comentario IMPORTANTE anterior, invoca protected virtual bool AuthorizeCore(HttpContextBase)
.
Para realizar cualquiera de las lógicas de AuthorizeCore a partir de un método de devolución de llamada de validación de caché estática, necesitaría conocer las propiedades de Usuarios y Roles de la instancia de AuthorizeAttribute. Sin embargo, no parece ser una forma fácil de conectar. Tendría que anular OnAuthorization para poner estos 2 valores en HttpContext (¿Colección de elementos?) Y luego anular OnCacheAuthorization para que vuelvan a salir. Pero eso huele sucio.
Si tenemos cuidado de utilizar la propiedad VaryByCustom = "User" en el atributo OutputCache, ¿podemos simplemente anular OnCacheAuthorization para que siempre devuelva HttpValidationStatus.Valid?Cuando el método de acción no tiene un atributo OutputCache, no tendríamos que preocuparnos de que esta devolución de llamada se haya invocado alguna vez, ¿correcto? Y si tenemos un atributo OutputCache sin VaryByCustom = "Usuario", entonces debería ser obvio que la página podría devolver cualquier versión almacenada en caché independientemente de la solicitud del usuario que creó la copia en caché. ¿Qué tan arriesgado es esto?
olive - aparte de mi respuesta a continuación, también puede buscar la publicación original de TheCloudlessSky donde obtuve la idea en mi código ... también desechar cualquier cosa innecesaria sobre la inyección de un servicio, o Sesiones ... todo eso es específico para mí Lo que importa es manejar la validación de la caché en esa función OnAuthorization() de la manera en que la necesita para funcionar. :) Cuídate. –
¿Qué ocurre si utiliza "UseSlidingExpiration = False" para imponer la caducidad absoluta? – lalibi
¿Sabes si este problema aún existe? Parece que lo estoy teniendo con MVC5 y, sin embargo, no parece tan común aparte de esta publicación. Parece realmente extraño que simplemente no funciona. No puedo imaginar el uso de caché y el caché de salida azul son tan infrecuentes – GraemeMiller