2009-05-15 12 views
11

que estoy tratando de conseguir [CompressFilter] trabajar con el almacenamiento en caché donut y se produzcan problemas.¿Puedo usar [CompressFilter] en ASP.NET MVC sin romper el almacenamiento en caché de rosquilla

Lo que pasa es que toda la página se almacena en caché y no sólo la rosquilla. La fuente para el CompressFilter que estoy usando está debajo. He cambiado esto desde el original source utilizar OnResultExecuted en lugar de OnActionExecuting() porque necesitaba acceso al tipo del resultado de evitar el almacenamiento en caché ciertas subclases ActionResult.

Mirando el código fuente MVC v1 real para OutputCacheAttribute parece que también está usando OnResultExecuted(), pero no creo que ese hecho esté causando el conflicto directamente.

No sé lo suficiente sobre cómo el almacenamiento en caché de sustitución trabaja para entender muy bien por qué se comporta como lo hace. Creo que es notable decir que esto no termina con ningún tipo de pantalla corrupta. ¡Simplemente se comporta como si no hubiera donuts!

Parece que tendré que usar algún tipo de plug-in II para manejar el almacenamiento en caché, lo cual realmente quería evitar tener que hacer, pero parece que también necesito el caché de donuts.

realidad estoy más interesado en este momento para saber por qué tiene este efecto, sino una solución si es posible sería grande también.

public class CompressFilter : ActionFilterAttribute 
{ 
    public override void OnResultExecuted(ResultExecutedContext filterContext) 
    { 
     HttpRequestBase request = filterContext.HttpContext.Request; 

     // dont encode images! 
     if (filterContext.Result is ImageResult) 
     { 
      return; 
     } 

     string acceptEncoding = request.Headers["Accept-Encoding"]; 

     if (string.IsNullOrEmpty(acceptEncoding)) return; 

     acceptEncoding = acceptEncoding.ToUpperInvariant(); 

     HttpResponseBase response = filterContext.HttpContext.Response; 

     if (acceptEncoding.Contains("GZIP")) 
     { 
      response.AppendHeader("Content-encoding", "gzip"); 
      response.Filter = new GZipStream(response.Filter, CompressionMode.Compress); 
     } 
     else if (acceptEncoding.Contains("DEFLATE")) 
     { 
      response.AppendHeader("Content-encoding", "deflate"); 
      response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress); 
     } 
    } 
} 
+0

+1 para que indique que ha mirado a través del código fuente. –

+0

@jordan gracias! solo desearía que no me hubiera tomado una hora darme cuenta de que el estúpido [CompressFilter] era lo que estaba en conflicto. Estaba comprobando todo lo demás posible como una causa para el almacenamiento en caché del buzón y realmente me hubiera gustado que este no hubiera sido el problema –

+0

+1 por señalarme este útil filtro. ¡Gracias! – jao

Respuesta

9

Eso es una mala implementación de la clase CompressFilter.

Por favor, lea esto: Finding Preferred Accept Encoding in C#

He escrito mi propia que obedecer la AcceptEncoding basado en el artículo anterior ::

public class CompressFilter : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     HttpRequestBase request = filterContext.HttpContext.Request; 

     string[] supported = new string[] { "gzip", "deflate" }; 

     IEnumerable<string> preferredOrder = new AcceptList(request.Headers["Accept-Encoding"], supported); 

     string preferred = preferredOrder.FirstOrDefault(); 

     HttpResponseBase response = filterContext.HttpContext.Response; 

     switch (preferred) 
     { 
      case "gzip": 
       response.AppendHeader("Content-Encoding", "gzip"); 
       response.Filter = new GZipStream(response.Filter, CompressionMode.Compress); 
       break; 

      case "deflate": 
       response.AppendHeader("Content-Encoding", "deflate"); 
       response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress); 
       break; 

      case "identity": 
      default: 
       break; 
     } 
    } 
} 

public class AcceptList : IEnumerable<string> 
{ 
    Regex parser = new Regex(@"(?<name>[^;,\r\n]+)(?:;q=(?<value>[\d.]+))?", RegexOptions.Compiled); 

    IEnumerable<string> encodings; 

    public AcceptList(string acceptHeaderValue, IEnumerable<string> supportedEncodings) 
    { 
     List<KeyValuePair<string, float>> accepts = new List<KeyValuePair<string, float>>(); 

     if (!string.IsNullOrEmpty(acceptHeaderValue)) 
     { 
      MatchCollection matches = parser.Matches(acceptHeaderValue); 

      var values = from Match v in matches 
         where v.Success 
         select new 
         { 
          Name = v.Groups["name"].Value, 
          Value = v.Groups["value"].Value 
         }; 

      foreach (var value in values) 
      { 
       if (value.Name == "*") 
       { 
        foreach (string encoding in supportedEncodings) 
        { 
         if (!accepts.Where(a => a.Key.ToUpperInvariant() == encoding.ToUpperInvariant()).Any()) 
         { 
          accepts.Add(new KeyValuePair<string, float>(encoding, 1.0f)); 
         } 
        } 

        continue; 
       } 

       float desired = 1.0f; 
       if (!string.IsNullOrEmpty(value.Value)) 
       { 
        float.TryParse(value.Value, out desired); 
       } 

       if (desired == 0.0f) 
       { 
        continue; 
       } 

       accepts.Add(new KeyValuePair<string, float>(value.Name, desired)); 
      } 
     } 

     this.encodings = from a in accepts 
         where supportedEncodings.Where(se => se.ToUpperInvariant() == a.Key.ToUpperInvariant()).Any() || a.Key.ToUpperInvariant() == "IDENTITY" 
         orderby a.Value descending 
         select a.Key; 
    } 

    IEnumerator<string> IEnumerable<string>.GetEnumerator() 
    { 
     return this.encodings.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return ((IEnumerable)this.encodings).GetEnumerator(); 
    } 
} 
+0

gracias.está aceptando la única diferencia? No tengo tiempo para compararlos ahora mismo. THX otra vez –

+0

Sí, casi. –

2

Reemplace el método OnResultExecuting. Esto se llama antes de renderizar ActionResult. Antes de verificar si el cliente acepta la compresión, compruebo el tipo de Resultado que trato de procesar. Si no es un ViewResult, no aplico ningún tipo de compresión.

Para que esto funcione, sus acciones tienen que llamar de forma explícita, ya sea Vista() o PartialView().

Aquí es lo que el CompressOutputAttrtibute parece:

public class CompressOutputAttribute : ActionFilterAttribute 
{ 
    public override void OnResultExecuting(ResultExecutingContext filterContext) 
    { 
     var result = filterContext.Result; 
     if (!(result is ViewResult)) 
      return; 

     HttpRequestBase request = filterContext.HttpContext.Request; 
     string acceptEncoding = request.Headers["Accept-Encoding"]; 
     if (string.IsNullOrEmpty(acceptEncoding)) 
      return; 

     acceptEncoding = acceptEncoding.ToUpperInvariant(); 

     HttpResponseBase response = filterContext.HttpContext.Response; 
     if (acceptEncoding.Contains("GZIP")) 
     {   
      // we want to use gzip 1st 
      response.AppendHeader("Content-encoding", "gzip"); 
      //Add DeflateStream to the pipeline in order to compress response on the fly 
      response.Filter = new GZipStream(response.Filter, CompressionMode.Compress); 
     } 
     else if (acceptEncoding.Contains("DEFLATE")) 
     { 
      //If client accepts deflate, we'll always return compressed content 
      response.AppendHeader("Content-encoding", "deflate"); 
      //Add DeflateStream to the pipeline in order to compress response on the fly 
      response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress); 
     } 
    } 
} 

dentro del controlador:

[CompressOutput] 
public class ArticleController : Controller 

    public PartialViewResult MostPopular() 
    { 
     var viewModel = ArticleMostPopularViewModel(); 
     viewModel.Articles = CmsService.GetMostPopularArticles(); 
     return PartialView(viewModel); 
    } 

    public ViewResult Show(int id) 
    { 
     var viewModel = ArticleShowViewModel(); 
     viewModel.Article = CmsService.GetArticle(id); 
     return View(viewModel); 
    } 
} 
+0

+1 para usar OnResultExecuting. Descubrí que aplicar compresión en OnActionExecuting causará problemas cuando se produzca un error porque el encabezado Content-Encoding se borra pero no filtra el flujo. –

Cuestiones relacionadas