2012-02-09 7 views
11

tengo un método de acción que yo quiero para almacenar en caché:OutputCache está enviando mal cabecera Vary cuando la llamada se acierta en la caché

[OutputCache(Duration=60*5, Location=OutputCacheLocation.Any, VaryByCustom="index")] 
public ActionResult Index() 
{ 
    return View(); 
} 

Con este enfoque:

public override string GetVaryByCustomString(HttpContext context, string custom) 
{ 
    context.Response.Cache.SetOmitVaryStar(true); 
    context.Response.Cache.VaryByHeaders["Cookie"] = true; 

    if (User.Identity.IsAuthenticated) 
    { 
     Debug.Print("Authenticated"); 
     context.Response.Cache.SetNoServerCaching(); 
     context.Response.Cache.SetCacheability(HttpCacheability.Private); 
     return null; 
    } 
    else 
    { 
     Debug.Print("Non authenticated"); 
     return custom; 
    } 
} 

La idea era keep a cached version of the page for non-authenticated users, but avoid caching for authenticated ones.

Pensé que siempre devolverá un Vary:Cookie encabezado HTTP, pero no lo es. Hacer una prueba con Fiddler y la emisión de dos veces la misma petición, en la primera HTTP llaman va bien:

HTTP/1.1 200 OK 
Cache-Control: public, max-age=300 
Content-Type: text/html; charset=utf-8 
Expires: Thu, 09 Feb 2012 10:53:36 GMT 
Last-Modified: Thu, 09 Feb 2012 10:48:36 GMT 
Vary: Cookie 
Server: Microsoft-IIS/7.5 
X-AspNetMvc-Version: 3.0 
X-AspNet-Version: 4.0.30319 
X-Powered-By: ASP.NET 
Date: Thu, 09 Feb 2012 10:48:37 GMT 
Content-Length: 441 

Sin embargo, en la segunda, se sobrescribe la cabecera:

HTTP/1.1 200 OK 
Cache-Control: public, max-age=297 
Content-Type: text/html; charset=utf-8 
Expires: Thu, 09 Feb 2012 10:53:36 GMT 
Last-Modified: Thu, 09 Feb 2012 10:48:36 GMT 
Vary: * 
Server: Microsoft-IIS/7.5 
X-AspNetMvc-Version: 3.0 
X-AspNet-Version: 4.0.30319 
X-Powered-By: ASP.NET 
Date: Thu, 09 Feb 2012 10:48:39 GMT 
Content-Length: 441 

Así que, como Hasta donde yo sé, los navegadores no almacenan en caché la solicitud, incluso si es pública, ya que Vary:* significa que la solicitud se ha generado con parámetros que no están en la URL ni en los encabezados HTTP. ¿Hay alguna manera de arreglar esto?

Atentamente.

ACTUALIZACIÓN:

De manera similar, cuando envío dos solicitudes autenticadas idénticos, la primera llamada se modificador private, pero no la cabecera Vary:

HTTP/1.1 200 OK 
Cache-Control: private, max-age=300 
Content-Type: text/html; charset=utf-8 
Expires: Thu, 09 Feb 2012 12:43:14 GMT 
Last-Modified: Thu, 09 Feb 2012 12:38:14 GMT 
Server: Microsoft-IIS/7.5 
X-AspNetMvc-Version: 3.0 
X-AspNet-Version: 4.0.30319 
X-Powered-By: ASP.NET 
Date: Thu, 09 Feb 2012 12:38:14 GMT 
Content-Length: 443 

Pero la segunda obtiene la misma respuesta que una solicitud no autenticada:

HTTP/1.1 200 OK 
Cache-Control: public, max-age=298 
Content-Type: text/html; charset=utf-8 
Expires: Thu, 09 Feb 2012 12:44:32 GMT 
Last-Modified: Thu, 09 Feb 2012 12:39:32 GMT 
Vary: * 
Server: Microsoft-IIS/7.5 
X-AspNetMvc-Version: 3.0 
X-AspNet-Version: 4.0.30319 
X-Powered-By: ASP.NET 
Date: Thu, 09 Feb 2012 12:39:33 GMT 
Content-Length: 443 

He subido un test project showing the issue, por lo tanto, puede intentarlo.

Tenga en cuenta que existe un pedido IHttpModule que establece una solicitud autenticada o no dependiendo de si la solicitud tiene una cookie o no, este no es un enfoque de "vida real", es solo para fines de prueba.

El proyecto contiene sólo una página web con un enlace a sí mismo, un enlace que conecta, y otro enlace que cierra la sesión:

  • entrada: envía una galleta en una redirección HTTP 302 para el hogar página otra vez
  • LogOut: envía de nuevo una cookie caducada en una reccion HTTP 302 a la página de inicio.

El espera/ideales comportamiento sería: acceso

  1. usuario Índice, y obtener la página del servidor. La página muestra la fecha "A".
  2. Índice de acceso de usuario nuevamente y el navegador muestra la versión en caché. La página muestra la fecha "A".
  3. Limpiar el caché del navegador.
  4. Índice de acceso de usuario nuevamente y el navegador muestra la versión en caché del servidor. La página muestra la fecha "A".
  5. El usuario hace clic en iniciar sesión, y el operador obtiene una nueva página, que muestra la fecha "B".
  6. El usuario hace clic en cerrar sesión y el navegador obtiene la página del servidor en caché. La página muestra la fecha "A" nuevamente.

Pero este es el comportamiento hasta el momento:

  1. el acceso del usuario Índice, y recibe la página del servidor. La página muestra la fecha "A".
  2. Índice de acceso de usuario nuevamente y el navegador muestra la versión en caché. La página muestra la fecha "A".
  3. Limpiar el caché del navegador.
  4. Índice de acceso de usuario nuevamente y el navegador muestra la versión en caché del servidor. La página muestra la fecha "A".
  5. El usuario hace clic en iniciar sesión, y el operador obtiene una nueva página, que muestra la fecha "B".
  6. El usuario hace clic en cerrar sesión, y el navegador debe obtener la página del servidor en caché, pero no. La página muestra la fecha "B" nuevamente desde el caché del navegador. Esto se debe a la falta del encabezado Vary en la respuesta autenticada.

No sé si me pasa algo de malo en el almacenamiento en caché, me falta algo de detalle o el OutputCache no funciona muy bien, pero agradecería cualquier orientación.

Saludos.

ACTUALIZACIÓN 2:

Mi intención es utilizar la semántica de caché HTTP para:

  1. Permitir a los navegadores y proxys caché de la versión "pública" de la página.
  2. Permitir que los navegadores guarden en caché la versión "autentificada" de la página para su usuario.

Si cambio de la declaración OutputCache para hacer el almacenamiento en caché sólo en el servidor y evitar que la corriente abajo y el cliente almacenamiento en caché:

[OutputCache(Duration=60*5, Location=OutputCacheLocation.Server, VaryByCustom="index")] 

se comporta como se esperaba, pero se impide que la corriente abajo y el cliente de caché, y eso no es lo que quiero

+1

¿Qué ocurre si también configura las otras propiedades de VaryBy en el atributo OutputCache del método? – bzlm

+0

He agregado VaryByHeader = "Cookie", y todavía sucede, la segunda llamada obtiene un Vary = *. – vtortola

+0

¿Qué pasa si prueba [el enfoque vinculado a en la pregunta vinculada] (http://visitmix.com/writings/using-varybycustom-with-outputcache-in-asp-net-mvc-to-support-caching-for- usuarios registrados) tal como están, sin variar por cookie o modificando los encabezados de respuesta dentro del método 'GetVaryByCustom' (Técnicamente, no es necesario que varíe por cookie para lograr el almacenamiento en caché solo para usuarios anónimos). – bzlm

Respuesta

4

no creo que el atributo [OutputCache] es lo que quiere, el método VaryByCustom es básicamente diciendo que quiero para almacenar en caché diferentes versiones sobre la base de estos parámetros, no tiene realmente una opción para de no caché y la mayoría del código en el atributo se basa en el almacenamiento en caché basado en el servidor.

Dicho esto la documentación sobre MSDN para almacenar en caché personalizada parece indicar que necesita para devolver una cadena a variar en función del estado de autenticación:

public override string GetVaryByCustomString(HttpContext context, string custom) 
{ 
    if(custom == "user") return "User:" + context.Request.User.Identity.Name; 

    return base.GetVaryByCustomString(context, custom); 
} 

Y a continuación, utilizar el usuario literal en el VaryByCustom :

[OutputCache(Duration=60*5, Location=OutputCacheLocation.Any, VaryByCustom="user")] 
public ActionResult Index() 
{ 
    return View(); 
} 

Así que básicamente esto se traduciría en una memoria caché que se está construyendo para Anonymous (asumiendo la identidad anónima es una cadena vacía o algo así) y cada usuario en el servidor, y un Vary: * enviado al cliente, creo. Obviamente no es ideal lo que estás buscando.

Si realmente desea almacenar en caché la versión no autenticada utilizando el almacenamiento en caché de HTTP, le recomendaría no usar el OutputCacheAttribute y usar algo más personalizado.

Desde aquí se puede simplemente escribir en su propio atributo personalizado algo así como lo que tiene para su aplicación GetVaryByCustomString (esto es sólo un pseudo código, necesitaría más de esto):

public class HttpCacheUnauthenticatedAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     if(!filterContext.HttpContext.Request.IsAuthenticated) { 
      //TODO: set unauthenticated caching values and vary here 
     } 
    } 
} 

Y luego etiquetar su acción método con él:

[HttpCacheUnauthenticated] 
public ActionResult Index() 
{ 
    return View(); 
} 
+0

El enfoque con OutputCache funciona como se esperaba en la primera llamada, simplemente es devolver el encabezado Vary incorrecto en llamadas posteriores, cuando las llamadas son un "golpe de caché" en OutputCache. Eso es todo. La funcionalidad se proporciona, pero no funciona como se esperaba. Implementar mi propio filtro de acción OutputCache no resolverá nada. El problema no es OutputCacheAttribute en sí mismo, sino la API de almacenamiento en caché de salida. – vtortola

+0

@vtortola Es por eso que creo que debe omitir la API y escribir encabezados usted mismo. ¿Solo busca el caché HTTP, no el caché del lado del servidor correcto? Como dije, el atributo OutputCache no está diseñado para ningún escenario en el que su contenido no se almacene en caché en una respuesta. –

+0

No, estoy buscando ambos: D Lea atentamente la sección "El comportamiento esperado/ideal sería:". Aclamaciones. – vtortola

0

estoy usando un proveedor de memoria caché personalizada y en este caso hay una solución simple para esto. Por BeginRequest, basado en el estado de autenticación de usuario, hemos creado una información de contexto para no correr caché:

HttpContext.Current.Items["NoCache"] = "1"; 

Y entonces nuestro método GetVaryBy volvemos nulo si esta información se establece:

public override string GetVaryByCustomString(HttpContext context, string custom) 
{ 
    if (HttpContext.Current.Items["NoCache"] != null) 
     return null; 

    // remaining code here 
} 

Y luego, en los métodos de caché, podemos probar lo mismo. Por ejemplo:

public override object Add(string key, object entry, DateTime utcExpiry) 
{ 
    if (HttpContext.Current.Items["NoCache"] != null) 
     return null; 

    // remaining code here 
} 
Cuestiones relacionadas