Как реализовать правильную обработку ошибок HTTP в .NET MVC 2? - PullRequest
38 голосов
/ 06 февраля 2011

Я весь день изо всех сил пытался реализовать обработку ошибок в моем приложении 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, если контроллер не может быть найден. Для случая 404 № ​​3 я попытался использовать собственный базовый контроллер для переопределения 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-resource-based-404-errors-in-aspnet-mvc/

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

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

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

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

Ответы [ 4 ]

48 голосов
/ 06 февраля 2011

Вот одна техника, которую вы могли бы использовать.Определите 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");
    }
}
4 голосов
/ 08 февраля 2011

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

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

0 голосов
/ 05 мая 2018

Это очень старый вопрос. но я подумал, что стоит, если я представлю вам гораздо более понятный способ обработки исключений 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.

0 голосов
/ 06 февраля 2011

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

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

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

...