Подтвердить что ты не робот

Как реализовать правильную обработку ошибок 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:

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

Вот то, что я считаю 500:

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

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

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

В обоих случаях я получаю 302 до 404. И я никогда не получаю 500 ошибок; если я модифицирую Web.config, чтобы поместить опечатку в конечную точку веб-службы, я все равно получаю 302, а 404 говорит, что URL (контроллер/действие), который использует веб-службу, не может быть найден. Я также получаю запрошенный URL как параметр (n нежелательный) querystring: /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?

4b9b3361

Ответ 1

Вот один из способов, который вы могли бы использовать. Определите 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");
    }
}

Ответ 3

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

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

Не является допустимым основанием для выброса 500, ее проблемы с проверкой данных и должны обрабатываться аннотациями данных MVC или каркасом проверки jQuery или т.д. Просто показывая сообщение об ошибке рядом с TextBox с надписью "User Name too long" намного лучше.