Как реализовать правильную обработку ошибок 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:
- Опубликовать отрицательное значение:
POST /User/New/new-user-name-too-long-for-db-column-constraint
- Проблема, не связанная с данными, как конечная точка веб-службы, не отвечающая
Некоторые из этих проблем должны быть идентифицированы конкретными контроллерами или моделями, а затем контроллеры должны выбросить соответствующее исключение 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?
Ответы
Ответ 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");
}
}
Ответ 2
Для HTTP 404 ошибок (без переадресаций) взгляните на мой пост в блоге по этому вопросу. Это может дать вам несколько хороших идей:
http://hectorcorrea.com/blog/returning-http-404-in-asp-net-mvc/16
Ответ 3
Это не отвечает на ваш вопрос, но важно отметить, что статус HTTP 500 указывает, что на сервере что-то пошло не так, поэтому ваш пример:
POST /User/New/new-user-name-too-long-for-db-column-constraint
Не является допустимым основанием для выброса 500, ее проблемы с проверкой данных и должны обрабатываться аннотациями данных MVC или каркасом проверки jQuery или т.д. Просто показывая сообщение об ошибке рядом с TextBox с надписью "User Name too long" намного лучше.