2009-05-15 8 views
11

Sto cercando di ottenere [CompressFilter] lavorando con caching ciambella e in esecuzione in problemi.Posso usare [CompressFilter] in ASP.NET MVC senza rompere la ciambella ciambella

Quello che succede è che l'intera pagina viene memorizzata nella cache e non solo la ciambella. La fonte per il CompressFilter che sto usando è sotto. L'ho modificato da original source per utilizzare OnResultExecuted anziché OnActionExecuting() perché avevo bisogno di accedere al tipo di risultato per evitare di memorizzare nella cache alcune sottoclassi ActionResult.

Guardando il vero codice sorgente MVC v1 per OutputCacheAttribute sembra che stia usando anche OnResultExecuted(), ma non penso che il fatto stia causando il conflitto direttamente.

Non so abbastanza su come la cache sostitutiva funzioni per capire esattamente perché si comporta come fa. Penso che sia degno di nota dire che questo non finisce con nessun tipo di display corrotto. Si comporta semplicemente come non c'è ciambella!

Sembra che dovrò usare un qualche tipo di plug-in di II per gestire il caching, che volevo davvero evitare di dover fare, ma sembra che abbia bisogno anche del caching in ciambella.

In questo momento sono più interessato a sapere perché ha questo effetto, ma una soluzione, se possibile, sarebbe anche ottima.

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 per affermare che si è guardato attraverso il codice sorgente. –

+0

@jordan, grazie! Vorrei solo che non mi ci fosse voluto un'ora per capire che lo stupido [CompressFilter] era ciò che era in conflitto. Stavo controllando tutto il resto possibile come una causa per il caching della ciambella in mancanza e vorrei davvero che questo non fosse stato il problema –

+0

+1 per avermi indirizzato a questo utile filtro. Grazie! – jao

risposta

9

Che è una cattiva implementazione della classe CompressFilter.

prega di leggere questo: Finding Preferred Accept Encoding in C#

ho scritto il mio che obbedire al AcceptEncoding sulla base del suddetto articolo ::

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

grazie.accetta la decodifica dell'unico differnece? Non ho tempo per confrontarli adesso. grazie ancora –

+0

Sì, praticamente. –

2

Sovrascrivo il metodo OnResultExecuting. Questo è chiamato prima di eseguire ActionResult. Prima di verificare se il client accetta la compressione, controllo il tipo di risultato che sto tentando di eseguire il rendering. Se non è un ViewResult, non applico alcun tipo di compressione.

Affinché questo funzioni, le azioni devono chiamare esplicitamente View() o PartialView().

Ecco ciò che il CompressOutputAttrtibute assomiglia:

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

all'interno del controller:

[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 per l'utilizzo di OnResultExecuting. Ho trovato che applicare la compressione in OnActionExecuting causerà problemi quando si verifica un errore perché l'intestazione Content-Encoding è deselezionata ma non il flusso del filtro. –

Problemi correlati