Пользовательские страницы ошибок ASP.NET MVC не отображаются в среде совместного размещения

У меня возникла проблема с пользовательскими ошибками в приложении ASP.NET MVC, которое я развернул на своем общем хосте. Я создал ErrorController и добавил следующий код в Global.asax, чтобы поймать необработанные исключения, зарегистрировать их и затем передать управление ErrorController для отображения пользовательских ошибок. Этот код взят из здесь:

protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    Response.Clear();

    HttpException httpEx = ex as HttpException;
    RouteData routeData = new RouteData();
    routeData.Values.Add("controller", "Error");

    if (httpEx == null)
    {
        routeData.Values.Add("action", "Index");
    }
    else
    {
        switch (httpEx.GetHttpCode())
        {
            case 404:
                routeData.Values.Add("action", "HttpError404");
                break;
            case 500:
                routeData.Values.Add("action", "HttpError500");
                break;
            case 503:
                routeData.Values.Add("action", "HttpError503");
                break;
            default:
                routeData.Values.Add("action", "Index");
                break;
        }
    }

    ExceptionLogger.LogException(ex); // <- This is working. Errors get logged

    routeData.Values.Add("error", ex);
    Server.ClearError();
    IController controller = new ErrorController();
    // The next line doesn't seem to be working
    controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}

Application_Error определенно срабатывает, потому что ведение журнала работает отлично, но вместо отображения моих пользовательских страниц ошибок я получаю общие файлы Go Daddy. Из заголовка сообщения в блоге выведенный выше код, я замечаю, что он использует Release Candidate 2 из структуры MVC. Что-то изменилось в 1.0, что делает последнюю строку кода неработоспособной? Как обычно, отлично работает на моей машине.

Любые предложения будут очень благодарны.

Изменить: забыл упомянуть, что я пробовал все 3 возможности для режима customErrors в Web.config(Off, On и RemoteOnly). Те же результаты независимо от этой настройки.

Изменить 2: И я также пробовал его с и без украшения [HandleError] на классах Controller.

Обновление: я выяснил и исправил 404s. На панели настроек Go Daddy Hosting Control Center имеется раздел панели настроек, в котором можно управлять поведением 404, а по умолчанию - показать свою общую страницу, и, по-видимому, это переопределяет любые настройки Web.config. Таким образом, моя пользовательская страница 404 теперь отображается по назначению. Однако 500 и 503 еще не работают. У меня есть код в HomeController, чтобы получить статическую текстовую версию содержимого, если Sql Server выдает исключение следующим образом:

public ActionResult Index()
{
    CcmDataClassesDataContext dc = new CcmDataClassesDataContext();

    // This might generate an exception which will be handled in the OnException override
    HomeContent hc = dc.HomeContents.GetCurrentContent();

    ViewData["bodyId"] = "home";
    return View(hc);
}

protected override void OnException(ExceptionContext filterContext)
{
    // Only concerned here with SqlExceptions so an HTTP 503 message can
    // be displayed in the Home View. All others will bubble up to the
    // Global.asax.cs and be handled/logged there.
    System.Data.SqlClient.SqlException sqlEx =
        filterContext.Exception as System.Data.SqlClient.SqlException;
    if (sqlEx != null)
    {
        try
        {
            ExceptionLogger.LogException(sqlEx);
        }
        catch
        {
            // couldn't log exception, continue without crashing
        }

        ViewData["bodyId"] = "home";
        filterContext.ExceptionHandled = true;
        HomeContent hc = ContentHelper.GetStaticContent();
        if (hc == null)
        {
            // Couldn't get static content. Display friendly message on Home View.
            Response.StatusCode = 503;
            this.View("ContentError").ExecuteResult(this.ControllerContext);
        }
        else
        {
            // Pass the static content to the regular Home View
            this.View("Index", hc).ExecuteResult(this.ControllerContext);
        }
    }
}

Вот код, который пытается получить статический контент:

public static HomeContent GetStaticContent()
{
    HomeContent hc;

    try
    {
        string path = Configuration.CcmConfigSection.Config.Content.PathToStaticContent;
        string fileText = File.ReadAllText(path);
        string regex = @"^[^#]([^\r\n]*)";
        MatchCollection matches = Regex.Matches(fileText, regex, RegexOptions.Multiline);
        hc = new HomeContent
            {
                ID = Convert.ToInt32(matches[0].Value),
                Title = matches[1].Value,
                DateAdded = DateTime.Parse(matches[2].Value),
                Body = matches[3].Value,
                IsCurrent = true
            };
    }
    catch (Exception ex)
    {
        try
        {
            ExceptionLogger.LogException(ex);
        }
        catch
        {
            // couldn't log exception, continue without crashing
        }
        hc = null;
    }

    return hc;
}

Я проверил, что если я изменил строку соединения для генерации исключения SqlException, код правильно регистрирует ошибку, а затем захватывает и отображает статический контент. Но если я также изменил путь к статическому текстовому файлу в Web.config, чтобы протестировать версию Home View 503, то вместо этого я получаю страницу, в которой нет ничего, кроме "недоступной службы". Это. Нет пользовательского сообщения 503 с внешним видом сайта.

Есть ли у кого-нибудь предложения по улучшению кода, который может помочь? Помогло бы добавить разные заголовки в HttpResponse? Или Go Daddy тяжело захватил 503s?

Ответ 1

Я нашел решение, и это невероятно просто. Оказывается, проблема была на самом деле в IIS7. При отладке этой проблемы в Visual Studio я увидел свойство объекта HttpResponse, которое я раньше не заметил:

public bool TrySkipIisCustomErrors { get; set; }

Это привело меня к ближайшей поисковой системе, которая показала отличную запись в блоге Рика Тралла, а другая - angrypets.com, а также этот вопрос здесь на SO. Эти ссылки объясняют детали gory намного лучше, чем я могу, но эта цитата из сообщения Рика довольно хорошо ее фиксирует:

Реальная путаница здесь возникает, потому что ошибка оказывается в ловушке ASP.NET, но в конечном счете все еще обрабатывается IIS, который смотрит на 500 и возвращает страницу ошибки IIS.

Также кажется, что это поведение специфично для IIS7 в интегрированном режиме. Из msdn:

При работе в классическом режиме в IIS 7.0 TrySkipIisCustomErrors значение свойства по умолчанию - true. При работе в интегрированном режиме Значение свойства TrySkipIisCustomErrors по умолчанию - false.

Таким образом, по существу, все, что мне нужно было сделать, это добавить Response.TrySkipIisCustomErrors = true; сразу после любого кода, который устанавливает Response.StatusCode в 500 или 503, и теперь все работает так, как было разработано.

Ответ 2

Я размещаю сайт ASP.NET MVC на GoDaddy, а также сталкивался с проблемами, связанными с настраиваемыми страницами ошибок. То, что я обнаружил посредством проб и ошибок, заключалось в том, что GoDaddy перехватывает ошибки на уровне HTTP.

Например, любая страница, которая вернула код состояния HTTP 404, заставила страницу пользовательской ошибки GoDaddy взять верх. В конце концов я изменил свои страницы ошибок, чтобы вернуть статус 200, и проблема, связанная с 404, исчезла. Мой HTML был тем же, просто нужно изменить статус HTTP.

Я, по общему признанию, никогда не пробовал делать то же самое с 503 ответами на состояние, но возможно, что такое же смягчение может сработать. Если вы измените статус 503 на статус 200, проблема исчезнет?

Обратите внимание, что если вы сделаете это обходное решение, вы хотите, чтобы поисковые системы не индексировали страницы ошибок, которые после возвращения статуса 200 будут неотличимы (с точки зрения поисковой системы) с обычной страницы. Поэтому не забудьте добавить тег META ROBOTS, чтобы предотвратить индексирование страниц ошибок, например.

<META NAME="ROBOTS" CONTENT="NOINDEX">

Недостатком этого подхода может быть то, что ваша страница может быть удалена из Google, что определенно не очень хорошо!

ОБНОВЛЕНИЕ:. Кроме того, вы также можете определить, является ли пользовательский агент искателем или нет, и если он искатель возвращает 503, а если он не искатель, верните 200. См. это сообщение в блоге для получения информации о том, как обнаружить сканеры. Да, я знаю, что возвращение другого контента для искателей и пользователей - это SEO-нет-нет, но я сделал это на нескольких сайтах без какого-либо плохого эффекта, поэтому я не уверен, какая проблема.

Выполнение обоих подходов (META ROBOTS и обнаружение ботов) может быть вашим лучшим выбором, в случае, если какие-либо искатели нечетного скольжения проскочат через детектор бота.