Фильтр сжатия MVC 3, приводящий к искаженному выводу - PullRequest
17 голосов
/ 28 декабря 2010

Итак, у меня есть собственный атрибут CompressAttribute, который настроен как глобальный фильтр в global.asax.Он использует рефлексию для проверки типа возвращаемого значения текущего метода действия и, если это «ViewResult», он сжимает выходные данные, используя GZip или Deflate.Это работает просто отлично, за исключением случаев, когда на странице выдается ошибка 500 Server.Если возникает ошибка, вместо отображения страницы ошибки .NET, я получаю кучу этого:

��������`I�% & / m� {J�J��t 100

Видимо, он пытается закодировать страницу 500 Ошибка сервера, которая вызывает проблемы.Какой лучший способ справиться с этим?

Вот код фильтра:

public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            MethodInfo actionMethodInfo = Common.GetActionMethodInfo(filterContext);
            if (GetReturnType(actionMethodInfo).ToLower() != "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"))
            {
                response.AppendHeader("Content-encoding", "gzip");
                response.Filter = new WebCompressionStream(response.Filter, CompressionType.GZip);
            }
            else if (acceptEncoding.Contains("DEFLATE"))
            {
                response.AppendHeader("Content-encoding", "deflate");
                response.Filter = new WebCompressionStream(response.Filter, CompressionType.Deflate);
            }
        }

Ответы [ 4 ]

21 голосов
/ 28 декабря 2010

Хорошо, поэтому я смог решить эту проблему, очистив свойство Response.Filter в событии Application_Error:

public void Application_Error(object sender, EventArgs e)
{
    Response.Filter.Dispose();
}

Интересно, есть ли более правильный способ сделать это, хотя ...

7 голосов
/ 15 марта 2012

Вы также можете решить эту проблему, добавив OnResultExecuting вместо OnActionExecuting.Это дает несколько преимуществ

  1. Вы можете обнаружить результат действия, не прибегая к рефлексии.
  2. OnResultExecuting не будет выполняться в исключительных случаях (MVC вызовет OnException, но неOnResultExecuting)

Примерно так:

public sealed class MyAttribute  : ActionFilterAttribute
{
    /// <summary>
    /// Called by MVC just before the result (typically a view) is executing.
    /// </summary>
    /// <param name="filterContext"></param>
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        var result = filterContext.Result;
        if (result is ViewResultBase)
        {
            var response = filterContext.HttpContext.Response;

            // Check your request parameters and attach filter.
        }
    }
1 голос
/ 29 декабря 2010

У меня была такая же проблема при просмотре asp.net mvc 1.0 для страницы, на которой был RenderAction (из сборки фьючерсов). Очевидно, проблема заключалась в том, что ответ кодировался дважды. Мне пришлось создать фильтр действий для этих дочерних действий, чтобы в коллекции DataTokens объекта RouteData был установлен флаг. Затем мне пришлось изменить фильтр сжатия, чтобы он возвращался в случае, если флаг был установлен. Мне также пришлось иметь дело с порядком выполнения фильтров. Может быть, это поможет, проверьте, вызывается ли фильтр сжатия более одного раза, когда возникает страница с ошибкой.

0 голосов
/ 03 сентября 2014

Принятый ответ не будет работать, если что-то уже было записано в вывод.

Вместо утилизации фильтра вы можете убедиться, что заголовки сохранены на месте:

 protected void Application_PreSendRequestHeaders()
{
    // ensure that if GZip/Deflate Encoding is applied that headers are set
    // also works when error occurs if filters are still active
    HttpResponse response = HttpContext.Current.Response;
    if (response.Filter is GZipStream && response.Headers["Content-encoding"] != "gzip")
        response.AppendHeader("Content-encoding", "gzip");
    else if (response.Filter is DeflateStream && response.Headers["Content-encoding"] != "deflate")
        response.AppendHeader("Content-encoding", "deflate");
}
...