Заголовок «Content-encoding» исчезает из ответа HttpHandler, если возникает исключение - PullRequest
6 голосов
/ 13 января 2012

У меня есть собственный HttpHandler, в котором я вручную включаю выходное сжатие, например:

context.Response.AppendHeader("Content-encoding", "gzip");
context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);

Это хорошо работает для большинства запросов, но при возникновении исключения заголовок «Content-encoding» исчезает изответ, пока фильтр сжатия остается на месте.В результате страница ошибки сжимается gzip, но браузер не получает заголовок, указывающий на этот факт.Затем браузер пытается отобразить все еще сжатые данные в виде текста, который gobbledygook .

Полный код тестового примера показан ниже.Попробуйте поочередно отключить сжатие или не выдавать исключение.

Может кто-нибудь пролить свет на то, почему заголовок «Content-encoding» исчезает?

Полагаю, я мог бы просто включить сжатие, как last , что делает обработчик, чтобы в случае обнаружения исключения оно никогда не достигало точки добавления фильтра сжатия;но поведение, которое я вижу, кажется мне ошибкой.Кто-нибудь может подтвердить?

public class TestHandler : IHttpHandler 
{
    public void ProcessRequest(HttpContext context)
    {
        CompressResponse(context);
        context.Response.Write("Hello world");

        // Throw an exception for testing purposes
        throw new Exception("Just testing...");
    }

    private void CompressResponse(HttpContext context)
    {
        string acceptEncoding = context.Request.Headers["Accept-Encoding"];
        if (String.IsNullOrEmpty(acceptEncoding))
        {
            return;
        }

        // gzip or wildcard
        if (acceptEncoding.ToLower().Contains("gzip") || acceptEncoding.Contains("*"))
        {
            context.Response.AppendHeader("Content-encoding", "gzip");
            context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
            return;
        }

        // Also handles deflate (not shown here)
        // <snip>
    }

    public bool IsReusable
    {
        get { return true; }
    }
}

РЕДАКТИРОВАТЬ: Скриншот все еще закодированного ответа, который я вижу с моим тестовым примером: http://i.imgur.com/49Vcl.png

Ответы [ 4 ]

1 голос
/ 05 мая 2014

У меня случалось то же самое при принудительном использовании gzip в приложении WebForms . Чтобы исправить это, мне пришлось очистить фильтр в методе Application_Error в Global.asax.cs

protected void Application_Error(Object sender, EventArgs e)
{
    Response.Filter = null;
}

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

0 голосов
/ 13 января 2012

Если у вас есть исключение, то сервер сбросит текущие установленные заголовки и контент, потому что они неверны, как вы, в конце концов, имеете исключение.

По крайней мере, ясно, что статус 200, который вы собирались отправить (потому что все успешные ответы, которые не меняют статус, отправляют 200, а после необработанного исключения он больше не был успешным), неверны, но все остальное связано с чем-то, что вы собирались сделать, но не смогли достичь, так что все неправильно и все идет.

Хотя фильтры не сбрасываются.

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

Вы не можете отправить заголовок, если вы позвонили Flush(), потому что хорошо, потому что вы сбросили. Куда пойдет заголовок?

0 голосов
/ 28 ноября 2013

Я тоже столкнулся с этой проблемой.Было сложно выследить.Я не уверен в точных особенностях всей этой ситуации, но я думаю, что происходит утечка памяти.

Когда вы впервые делаете это:

context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);

вы назначаете неуправляемый ресурс до Filter.Обычно это будет заключено в оператор using, чтобы он был правильно расположен в случае, если что-то пошло не так .

Итак, когда что-то идет не так, возникает проблема.Filter содержит поток, который все еще открыт, даже несмотря на то, что в ответ записывается желтый экран смерти.За этим следует безумие (как показано на скриншоте).

Не бойся!На самом деле существует простой способ преодолеть эту проблему.Утилизируйте фильтр.К счастью, уже есть место для применения этой глобальной проверки для удаления фильтров.

global.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
        filters.Add(new HandleErrorAttribute());//default handler
        filters.Add(new HandleErrorEncodingAttribute());//extra check for filter disposal
}

пространство имен обработчика ошибок

public class HandleErrorEncodingAttribute : FilterAttribute, IExceptionFilter
{
    public virtual void OnException(ExceptionContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }
        if (filterContext.IsChildAction)
        {
            return;
        }
        // If custom errors are disabled, we need to let the normal ASP.NET exception handler
        // execute so that the user can see useful debugging information.
        if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
        {
            filterContext.HttpContext.Response.Filter.Dispose();//fixes response stream
            return;
        }
    }
}
0 голосов
/ 13 января 2012

Я проверяю ваш код и не могу найти никаких проблем. Да, gzip не установлен, но фильтр также не установлен, и asp получает управление и отправляет ошибку.

Принудительная очистка заголовка создает реальную проблему

 CompressResponse(context);
 context.Response.Flush(); 

Если я заставлю заголовок gzip, то страница будет отображаться неправильно.

Двое думают, что здесь может быть ваша проблема. Вы не установили кодировку страницы

context.Response.ContentEncoding = new UTF8Encoding();

и у вас нет установленного ContentType

context.Response.ContentType = "text/plain";

Возможно, отчасти это является причиной того, что вы получаете не исправленный рендеринг страницы. Однако в моих тестах даже при том, что описанная вами проблема не появляется.

...