Как реализовать правильную обработку ошибок HTTP в .NET MVC 2?

Я весь день пытался реализовать обработку ошибок в моем приложении ASP.NET MVC 2. Я изучил множество техник, но ни одна из них не работает должным образом. Я использую MVC2 и .NET 4.0 (начал проект до того, как был выпущен MVC3; мы обновим его после того, как поставим наш первоначальный выпуск).

На этом этапе я буду счастлив правильно обработать ошибки 404 и 500 - тоже подойдет ошибка 403 (требуется авторизация), за которой последуют различные другие конкретные ответы. Прямо сейчас я получаю либо все 404, либо все 500, либо все 302 до 404, либо все 302 до 500.

Вот мои требования (которые должны быть довольно близки к основным требованиям HTTP):

  • Если ресурс не найден, вывести 404 и отобразить страницу, относящуюся к 404, с запрошенным URL. НЕ возвращайте промежуточный код ответа, например 302. В идеале, сохраните запрошенный URL, а не показывайте новый URL, например /Error/NotFound, но если последний отображается, убедитесь, что мы не вернули ответ перенаправления, чтобы получить его.

  • Если произошла внутренняя ошибка сервера, выведите 500 и отобразите ошибку 500 с некоторым указанием того, что пошло не так. Опять же, не возвращайте промежуточный код ответа и, в идеале, не меняйте URL-адрес.

Вот что я бы назвал 404:

  1. Статический файл не найден: /Content/non-existent-dir/non-existent-file.txt
  2. Контроллер не найден: /non-existent-controller/Foo/666
  3. Контроллер найден, но действие не найдено: /Home/non-existent-action/666
  4. Контроллер и действие найдены, но действие не может найти запрошенный объект: /Home/Login/non-existent-id

Вот что я бы назвал 500:

  1. Опубликовать неверное значение: POST /User/New/new-user-name-too-long-for-db-column-constraint
  2. Проблема, не связанная с данными, например, не отвечает конечная точка веб-службы.

Некоторые из этих проблем необходимо идентифицировать с помощью конкретных контроллеров или моделей, а затем контроллеры должны генерировать соответствующее исключение HttpException. С остальным следует обращаться более обобщенно.

Для случая 404 № ​​2 я попытался использовать настраиваемый ControllerFactory для выдачи 404, если контроллер не может быть найден. Для случая №3 404 я попытался использовать настраиваемый базовый контроллер, чтобы переопределить HandleUnknownAction и выдать ошибку 404.

В обоих случаях я получаю 302 перед 404. И я никогда не получаю 500 ошибок; если я изменю Web.config, чтобы вставить опечатку в конечную точку моей веб-службы, я все равно получу 302, затем 404, в котором указывается, что URL-адрес (контроллер / действие), который использует, веб-служба не может быть найдена . Я также получаю запрошенный URL как (n нежелательных) параметров строки запроса: /Error/NotFound?aspxerrorpath=/Home/non-existent-action

Оба эти метода пришли из http://www.niksmit.com/wp/?p=17 (Как получить обычные страницы с ошибкой 404 (Страница не найдена) с помощью ASP.Net MVC), на которую указывает http://richarddingwall.name/2008/08/17/strategies-for-наосновересурсов-404-ошибок-в-aspnet-mvc/

Если в Web.config у меня есть <customErrors mode="On" defaultRedirect="~/Error/Unknown" redirectMode="ResponseRedirect" />, я получаю соответствующий код ответа, но мой контроллер ошибок никогда не вызывается. Удаление атрибута redirectMode дает мне представление об ошибках MVC, но с промежуточным 302 и измененным URL - и всегда с тем же контроллером (Unknown = 500; если я изменю его на NotFound, все будет выглядеть как 404).

Вот еще кое-что, что я прочитал и попытался реализовать:

.. вместе с кучей сообщений StackOverflow.

Мне кажется, такая обработка ошибок довольно проста для веб-приложений, и у инфраструктуры MVC должны быть настройки по умолчанию, которые делают это из коробки и позволяют людям расширять ее для работы в противном случае. Возможно, они сделают это в будущем выпуске. А пока кто-нибудь может дать мне исчерпывающие сведения о том, как реализовать правильные HTTP-ответы?


person Val    schedule 06.02.2011    source источник


Ответы (4)


Вот один из приемов, который вы можете использовать. Определите ErrorsController, который будет обслуживать страницы с ошибками:

public class ErrorsController : Controller
{
    public ActionResult Http404()
    {
        Response.StatusCode = 404;
        return Content("404", "text/plain");
    }

    public ActionResult Http500()
    {
        Response.StatusCode = 500;
        return Content("500", "text/plain");
    }

    public ActionResult Http403()
    {
        Response.StatusCode = 403;
        return Content("403", "text/plain");
    }
}

а затем в Global.asax вы можете подписаться на событие Application_Error, где вы можете зарегистрировать исключение и выполнить соответствующее действие ErrorsController:

protected void Application_Error(object sender, EventArgs e)
{
    var app = (MvcApplication)sender;
    var context = app.Context;
    var ex = app.Server.GetLastError();
    context.Response.Clear();
    context.ClearError();
    var httpException = ex as HttpException;

    var routeData = new RouteData();
    routeData.Values["controller"] = "errors";
    routeData.Values["exception"] = ex;
    routeData.Values["action"] = "http500";
    if (httpException != null)
    {
        switch (httpException.GetHttpCode())
        {
            case 404:
                routeData.Values["action"] = "http404";
                break;
            case 403:
                routeData.Values["action"] = "http403";
                break;
            case 500:
                routeData.Values["action"] = "http500";
                break;
        }
    }
    IController controller = new ErrorsController();
    controller.Execute(new RequestContext(new HttpContextWrapper(context), routeData));
}

И теперь все, что осталось, это начать выбрасывать правильные исключения:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        throw new HttpException(404, "NotFound");
    }
}
person Darin Dimitrov    schedule 06.02.2011
comment
Выглядит хорошо - я попробую через несколько часов и дам вам знать, как это работает. Одного я не вижу: где вы обрабатываете случай, когда контроллер не найден? - person Val; 06.02.2011
comment
@Val, когда контроллер не найден, ASP.NET MVC автоматически генерирует исключение HttpException с кодом 404, поэтому он войдет в первый случай. - person Darin Dimitrov; 07.02.2011
comment
Хорошо, я реализовал основы, и теперь я возвращаюсь к реализации пользовательских представлений ошибок для различных ошибок HTTP и передачи соответствующей информации для отображения в каждой из них. У меня есть больше работы, чтобы связать это - создать модель представления ошибок, заполнить ее в ErrorController и т. Д., Но базовая функциональность отлично работает. Нет 302, и, как вы отметили, недостающие действия и контроллеры обрабатываются должным образом. Спасибо - это самый простой и чистый, правильно работающий пример, который я когда-либо видел! - person Val; 07.02.2011
comment
Как всегда ответ @DarinDimitrov выдающийся! Теперь я получаю ответ 404 в Firebug вместо 302. - person Leniel Maccaferri; 18.03.2012
comment
@Darin: Можно ли это сделать и в MVC3, или есть другой способ сделать это? Если да, то каким образом? - person Brendan Vogt; 20.04.2012
comment
Он отлично работает на локальном компьютере, но когда я развертываю его на iis 7.5, отображается asp.net 4.5 и mvc4, страница iis 404. Как я могу это исправить? Спасибо - person Barbaros Alp; 19.06.2013
comment
Привет, я опаздываю на вечеринку, применима ли эта же структура к MVC 4, или есть некоторые вещи, которые, по словам @Val, должны быть по умолчанию в MVC, были реализованы с тех пор, как был опубликован этот ответ? - person ganders; 10.07.2014
comment
У меня работало на 5.2.3.0. Спасибо, Дарин, у тебя есть настоящая MVC-фу. - person Martin Hansen Lennox; 12.09.2019

В случае ошибок HTTP 404 (без перенаправления) ознакомьтесь с моим сообщением в блоге по этой теме. Это может дать вам несколько хороших идей:

http://hectorcorrea.com/blog/returning-http-404-in-asp-net-mvc/16

person Hector Correa    schedule 08.02.2011
comment
Отлично - вы делаете то, что рекомендовал Дарин, хотя у него есть дополнительная инфраструктура, позволяющая обрабатывать несколько типов исключений. Сейчас я пытаюсь переместить его логику из Application_Error в сам ErrorController. - person Val; 08.02.2011
comment
Контент больше не доступен - person ; 14.09.2017
comment
@Darrren Ссылка снова доступна. - person Hector Correa; 14.09.2017

Это не отвечает на ваш вопрос, но важно отметить, что статус HTTP 500 указывает на то, что на сервере что-то пошло не так, поэтому ваш пример:

POST /User/New/new-user-name-too-long-for-db-column-constraint

Не является веским основанием для выдачи 500, это проблема с проверкой данных и должна обрабатываться аннотациями данных MVC, средой проверки jQuery и т. Д. Намного лучше просто показать сообщение об ошибке рядом с TextBox, в котором говорится, что «Имя пользователя слишком длинное».

person JK.    schedule 06.02.2011
comment
Да, я должен проверить и выдать ошибку проверки. Но если мне не удается проверить и получить ошибку ограничений из БД, когда не удается вставить или обновить строку, и я не проверяю это должным образом, я хочу выдать внутреннюю ошибку сервера. Фактически, вот как я обнаружил, что нам не хватает некоторой проверки! - person Val; 06.02.2011

Это очень старый вопрос. но я подумал, что оно того стоит, если я познакомлю вас с гораздо более чистым способом обработки исключений Http, который я видел в уважаемом " Ответ Джесси Уэбба ".

Решение - использовать элемент httpErrors раздела system.webServer:

<httpErrors errorMode="Custom" existingResponse="Replace">
  <remove statusCode="404" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
  <error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>

Таким же образом можно регистрировать все исключения. "Прочтите " ответ Джесси Уэбба "".

Это действительно намного чище и работает так же хорошо, как и любое другое решение (без перенаправления).

Примечание. Это работает только в IIS 7 и новее. (Из-за недавно добавленного элемента httpErrors.

person Arad    schedule 05.05.2018