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

я боролся весь день, чтобы реализовать обработку ошибок в моем ASP.NET MVC 2 app. Я просмотрел множество методов, но ни один из них не работает должным образом. Я использую 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 нежелательный) querystring param:/Error/NotFound?aspxerrorpath=/Home/non-existent-action

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

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

вот некоторые из других вещей, которые я читал и пытался вместе с кучей сообщений StackOverflow.

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

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");
    }
}

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

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


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

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

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


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

решение заключается в использовании httpErrors элемент :

<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 элемент, который был недавно добавлен.