Могу ли я использовать [CompressFilter] в ASP.NET MVC без нарушения кэширования пончиков - PullRequest
11 голосов
/ 15 мая 2009

Я пытаюсь заставить [ CompressFilter ] работать с кэшированием пончиков и сталкиваться с проблемами.

Что происходит, так это то, что вся страница кэшируется, а не только пончик. Источник для CompressFilter, который я использую, указан ниже. Я изменил это из исходного источника , чтобы использовать OnResultExecuted вместо OnActionExecuting(), потому что мне нужен был доступ к типу результата, чтобы избежать кэширования определенных подклассов ActionResult.

Глядя на фактический исходный код MVC v1 для OutputCacheAttribute, похоже, что он также использует OnResultExecuted(), но я не думаю, что этот факт непосредственно вызывает конфликт.

Я недостаточно знаю, как работает кэширование замещения, чтобы понять, почему оно ведет себя так, как оно работает. Я думаю, что стоит сказать, что это не приведет к повреждению дисплея. Просто ведет себя так, как будто пончика нет!

Похоже, мне придется использовать какой-то плагин IIs для обработки кэширования, чего я действительно хотел избежать, но похоже, что мне нужно и кэширование пончиков.

На самом деле мне сейчас интереснее узнать, почему он имеет такой эффект, но если возможно, было бы неплохо и решение.

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

Ответы [ 2 ]

9 голосов
/ 13 октября 2009

Это плохая реализация класса CompressFilter.

Пожалуйста, прочтите это: Поиск предпочтительной кодировки принятия в C #

Я написал свою собственную, которая будет подчиняться кодировке AcceptEncoding, основанной на статье выше:

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();
    }
}
2 голосов
/ 12 июня 2009

Я переопределяю метод OnResultExecuting. Это вызывается до рендеринга ActionResult. Прежде чем проверять, принимает ли клиент сжатие, я проверяю тип результата, который я пытаюсь отобразить. Если это не ViewResult, я не применяю никакой компресс.

Чтобы это работало, ваши Действия должны явно вызывать либо View (), либо PartialView ().

Вот как выглядит CompressOutputAttrtibute:

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

Внутри контроллера:

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