Как отправить код состояния 500 для общей страницы ошибки в IIS?

Я использую общую страницу ошибок, используя директиву ASP.NET <customErrors>.

<customErrors mode="On" defaultRedirect="500.html" redirectMode="ResponseRewrite">
</customErrors>

Проблема - при возникновении ошибки эта страница не возвращает HTTP-статус "500". Он приходит как 200. Таким образом, программы проверки ссылок и пауки не видят, что есть какая-то проблема.

Как я могу отправить статус HTTP 500 вместе со статической страницей 500.html?

Требования:

  • Я должен использовать redirectMode="ResponseRewrite"
  • Я не могу использовать динамическую страницу, только статический .html.

person frankadelic    schedule 18.08.2010    source источник


Ответы (6)


В документации MSDN для элемента customErrors указано, что он реализован System.Web.Configuration.CustomErrorsSection. Если мы используем Red Gate .NET Reflector для анализа этого класса, мы можем увидеть, где этот параметр используется в Framework.

Он используется System.Web.UI.Page.HandleError и System.Web.HttpResponse.ReportRuntimeError.

Оба они заканчиваются вызовом System.Web.HttpResponse.RedirectToErrorPage. (Название этого метода сбивает с толку: важно отметить, что RedirectToErrorPage принимает параметр redirectMode в качестве параметра, поэтому он вызывается, даже если вы используете ResponseRewrite и на самом деле перенаправление не происходит.)

Соответствующая часть метода RedirectToErrorPage:

    if (redirectMode == CustomErrorsRedirectMode.ResponseRewrite)
    {
        this.Context.Server.Execute(url);
    }

Кажется, нет никакого способа установить код ответа при обработке ошибок: в конце концов, это просто Server.Execute. Поэтому кажется неизбежным, что вам нужно будет написать код для получения желаемого HTTP-ответа.

Можете ли вы пересмотреть, почему вы хотите использовать простой файл .html? Это кажется разумным выбором для обработки ошибок, потому что вы не хотите проходить через все накладные расходы страницы .aspx, когда это может привести к возникновению другой ошибки.

Но, возможно, есть какая-то золотая середина, которая будет столь же надежной, как файл .html?

Например, вы можете создать предварительно скомпилированный HttpHandler, зарегистрировать его по URL-адресу /500.error, а затем сделать 500.error страницей перенаправления по умолчанию. (Это будет похоже на то, как работает ScriptResource.axd.) Если вы предварительно скомпилируете свой модуль в DLL (в отличие от компиляции на лету из простого старого файла .axd), вы можете обнаружить, что он так же надежен в состояние ошибки. Если вы столкнулись с ошибкой, при которой даже это не сработает, то статический файл .html, вероятно, тоже не сработает — имейте в виду, что директива customErrors по-прежнему полагается на .NET, работающий под капотом, и по-прежнему использует StaticFileHandler для обслуживания ваш .html файл.

В качестве альтернативы вы можете рассмотреть обратный прокси-сервер перед вашим приложением IIS, который будет обслуживать дружественные 500 страниц даже в случае катастрофического сбоя пула приложений. Это потребовало бы больше работы для настройки, но было бы даже более надежным, чем customErrors, например. если ваш web.config будет поврежден, даже customErrors не будут работать.

person David James    schedule 27.08.2010
comment
Вы хорошо заметили, что ‹customErrors› зависит от ASP.NET. Я использовал 500.html, чтобы обойти зависимость от ASP.NET, но это спорно, если ASP.NET не отвечает. Я думаю, что лучше всего использовать 500.aspx в ‹customErrors› и настроить резервную страницу 500.html Error Page на уровне IIS на случай, если ASP.NET не работает. - person frankadelic; 23.12.2010

В файле gllobal.asax добавьте следующий код

protected void Application_EndRequest(object sender, EventArgs e)
    {
        if (Request.Url.AbsolutePath.EndsWith("500.html"))
            Response.StatusCode = 500;
    }
person Vinay B R    schedule 21.08.2010
comment
Требуется ли для этого интегрированный конвейер IIS7 +? В противном случае запросы для .html не запускали бы Application_EndRequest.. - person frankadelic; 23.08.2010
comment
извините, я не обратил внимания на то, что расширение страницы с ошибкой является html, есть ли проблема с тем, что это страница aspx. в этом случае вам не придется беспокоиться об IIS. Также, если я не ошибаюсь, у iis 6 также должны быть способы перенаправления html-запроса на обработчики aspnet. у меня сейчас нет доступа к машине с установленным iis 6 - person Vinay B R; 23.08.2010
comment
см. мой второй пункт... это должен быть статический HTML-файл. - person frankadelic; 24.08.2010
comment
Не уверен, что это не может быть страница aspx. вы можете попробовать это в iis 6, вы можете перенаправить html-запросы на asp.net, изменив конфигурацию приложения вашего веб-сайта следующим образом. В свойствах вашего сайта на вкладке HomeDirectory нажмите кнопку конфигурации. Вы можете сопоставить html с dll aspnet_isapi. - person Vinay B R; 24.08.2010

Хорошо, у меня есть решение, единственный способ заставить его работать — обойти раздел пользовательских ошибок в файле веб-конфигурации.

Итак, пример default.aspx

public partial class Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        throw new Exception("boom");
    }
}

Затем в файле global.asax:

protected void Application_Error(object sender, EventArgs e)
{
    // Clear the error to take control of process
    Server.ClearError();

    Response.WriteFile(Server.MapPath("500.html"));
    Response.StatusCode = 500;
    Response.StatusDescription = "Internal Server Error";
}

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

Проверено только с веб-сервером VS Cassini, однако я не вижу причин, по которым это не должно работать в iis6.

person Adam Jenkin    schedule 24.08.2010

Попробуйте настроить свой пользовательский раздел ошибок следующим образом:

<customErrors mode="On" redirectMode="ResponseRewrite">
  <error statusCode="500" redirect="500.aspx">
</customErrors>

В файле 500.aspx измените код ответа в page_load;

Response.StatusCode = 500;
Response.StatusDescription = "Internal Server Error";
person Adam Jenkin    schedule 19.08.2010
comment
Мне нужно использовать redirectMode=ResponseRewrite, так что это не вариант для меня. - person frankadelic; 20.08.2010
comment
Кроме того, ваше решение не отправляет HTTP-статус 500... Как написано, оно выполняет перенаправление 302 на 500.html. Сам 500.html имеет HTTP-статус 200. Проверьте это в firebug. - person frankadelic; 20.08.2010
comment
измененный ответ на вопрос. - person Adam Jenkin; 23.08.2010
comment
Теперь я попробовал вышеописанное с 404 вместо 500 (проще сгенерировать), и метод определенно работает. ResponseRewrite гарантирует отсутствие перенаправлений, и устанавливается StatusCode (проверено с помощью построителя ответов в скрипаче) - person Adam Jenkin; 24.08.2010
comment
Адам, ваш код перенаправляет на файл 500.aspx. См. мои требования — это должен быть статический файл .html, а не .aspx. - person frankadelic; 24.08.2010
comment
Извинения, откровенно говоря, я заметил это прошлой ночью. Я пытался заставить его работать со статическим файлом, используя метод server.transfer(), но не смог изменить код состояния. интересно, как вы генерируете свою ошибку 500 для тестирования? - person Adam Jenkin; 24.08.2010
comment
Чтобы сгенерировать ошибку 500, вы можете просто создать исключение на странице ASPX. Это вызывает ошибку 500. - person frankadelic; 24.08.2010

Это можно сделать с помощью isapi-фильтра. Его нужно было бы написать на c, но он может изменить статус ответа на запрос .html, чтобы браузер получил 500 с вашей пользовательской html-страницей.

person Mike    schedule 27.08.2010

Если вы настаиваете на изменении базовой HTTP-сигнализации, вам придется либо уступить некоторым требованиям, либо быть готовым написать собственный веб-сервер. Да, вам нужно запустить интегрированный в IIS7 AppPool, и вам все равно, возможно, придется принять перенаправление на активную страницу, поскольку вы пытаетесь имитировать, что ваш сервер не работает, а сервер не предназначен для имитации самоубийства. Поэтому, если это дает вам один простой способ сделать это, вы можете либо сказать спасибо, либо разработать свой собственный, несовместимый с HTTP сервер, который будет шифровать коды ответов по желанию.

person ZXX    schedule 26.08.2010
comment
Попробуйте следующее: создайте классическую страницу .asp с делением на ноль (1/0) и выполните ее. Вы увидите, что возвращается статическая страница с ошибкой, а статус HTTP действительно равен 500 (подтвердите с помощью firebug или fiddler2). Это не смерть/самоубийство сервера, это просто означает, что произошла ошибка. Никакая базовая http-сигнализация не была изменена. Вы можете перенастроить содержимое страницы ошибок с помощью диспетчера IIS › Страницы ошибок... Моя проблема заключается в том, что этот параметр не применяется к страницам .aspx. Я могу поместить перенаправление на файл .html в ‹customErrors› в web.config, но результирующая страница возвращает статус 200. - person frankadelic; 23.12.2010
comment
Вы говорите, что для внутренней ошибки сервера (например, из-за исключения) правильный код состояния http - 302? или 200? возвращает код состояния 302 или 200 НЕ чрезвычайно зашифрованный? :-› - person Myster; 22.03.2011
comment
Единственное, что зашифровано, — это обработка ASP.NET кодов ошибок. Генерация исключения и возврат и страница с ошибкой не должны давать код ответа 200 или 302. Это должно быть 500, чтобы указать, что что-то сломалось. - person Gerald Davis; 18.03.2014