Andrey on .NET | Упрощаем обработку ошибок в ASP.NET MVC приложении.

Упрощаем обработку ошибок в ASP.NET MVC приложении.

Каждый раз, при создании нового проекта, приходится проделывать ряд одних и тех же определенных шагов. И вполне естественно, что хочется минимизировать их количество. Давайте посмотрим как можно упросить процесс обработки ошибок и показа сообщений о них.

Что должно происходить при ошибке в веб-приложении? Необходимо занести информацию о ней в журнал, уведомить администратора сайта, а посетителю показать понятную ему страницу с извинениями.

Как сделать описанное выше наверное знают многие. Но не писать же каждый раз один и тот же код? Поэтому воспользуемся библиотекой Moveax.Mvc.ErrorHandler, которая позволяет легко добавлять страницы с сообщениями об ошибках.

Установка

Для её установки выполним в консоли NuGet следующую команду:

PM> Install-Package Moveax.Mvc.ErrorHandler

По завершению в проект будут добавлены Контроллер ErrorController и два Представления:

  • Http404 – страница для сообщения о несуществующем пути;
  • Default – страница по-умолчанию, для всех ошибок у которых нет отдельных Представлений.

Разумеется, в дальнейшем их можно изменить, как того потребует дизайн веб-приложения.

Для завершения установки необходимо в global.asax.cs добавить код для перехвата исключений:

protected void Application_Error(object sender, System.EventArgs e)
{
    var errorHandler = new MvcApplicationErrorHandler(application: this, exception: this.Server.GetLastError()) {
        EnableHttpReturnCodes = false,
        PassThroughHttp401 = false
    };

    errorHandler.Execute();
}

Поведение обработчика ошибок MvcApplicationErrorHandler можно настроить с помощью двух свойств:

  • EnableHttpReturnCodes – определяет будет ли клиенту возвращаться код ошибки (true), или просто страница с уведомлением (false);
  • PassThroughHttp401 – указывает на необходимость игнорировать ошибку HTTP 401, если в приложении предусмотрена отдельная схема для её обработки.

Настройка

При возникновении внештатной ситуации, логика работы ErrorController следующая:

  • каждый раз вызывается метод HandleError(), в которые передается описание ошибки. Здесь можно вставить код, например, ведения журнала;
  • вызывается Действие с именем "Http[код ошибки]" (например, Http404);
  • если такого Действия нет, то используется Действие Default.

Как легко понять, для добавления отдельной страницы с сообщением для конкретной ошибки, достаточно создать Действие и Представление с именем в формате "Http"+[код ошибки]. Пример можно увидеть в самом ErrorController:

public ActionResult Http404(ErrorDescription errorDescription)
{
    return this.View();
}

Класс ErrorDescription содержит информацию об возникшей ошибке:

public class ErrorDescription
{
    public int HttpCode { get; }
    public Exception Exception { get; }
    public HttpRequest Request { get; }
}

Таким образом, библиотека Moveax.Mvc.ErrorHandler позволяет свести всю работу к добавлению Действий и вызову методов ведения журнала.

Обратная связь

Вопросы, советы, предложения принимаются по почте или на сайте проекта.

Комментарии (19) -

Алексей 03.10.2013 21:25:52

Этого мало.

@ Алексей: Предлагайте.

Михаил 11.10.2013 17:43:06

Как то странно, ставлю в Global.asax обработку на Application_Error, ошибку выдаёт текстом(теги видны), т.е. не разметкой. Ставлю на Application_EndRequest всё нормально, разметка есть. Делаю в коде экшена return new HttpNotFoundResult("error"); Выпадает в 500 ошибку. В чём может быть проблема?

@ Михаил: Странное поведение. Особенно в плане Application_EndRequest. Это вообще событие другого плана. Что-то еще вмешивается в обработку?

Михаил 06.12.2013 19:40:43

Andrey :
@ Михаил: Странное поведение. Особенно в плане Application_EndRequest. Это вообще событие другого плана. Что-то еще вмешивается в обработку?

Да, что-то глюкнуло. Сейчас всё норм. Хотел поинересоваться, а как мне вызвать ошибку в самом контроллере? т.е. например мне надо получить какой-то объект по ID, а его нет в БД. И я бы хотел вызвать 404 ошибку.

@ Михаил: Response.StatusCode = (int)HttpStatusCode.NotFound;
или
return new HttpStatusCodeResult((int)HttpStatusCode.NotFound, "some text")
или
return new HttpNotFoundResult();


Михаил 06.12.2013 22:11:04

@ Andrey: Да пробовал эти варианты, не перехватывает ошибку. Вернул и вернул такой результат.

@ Михаил: В смысле надо вызвать 404 страницу ErrorController? Тогда RedirectToAction().

Виктор 24.02.2014 15:03:45

Удобно, спасибо.
Но вот обрабатывает не все исключения. Например, те что генерятся в действиях контроллеров (в том числе и вызываемые через ajax-запросы), протоколируются.
А вот хитрая ошибка, когда не был настроен https на сервере, не запротоколировалась:

System.Net.WebException
The request was aborted: Could not create SSL/TLS secure channel.


При этом вместо вьюхи Default из ErrorController'а сработала дефолтная вьюха Shared/Error...

Для не особо опытных в asp.net mvc (типа меня Smile ) вот минимальная разметка для вьюхи Default, чтобы не заморачиваться:


@{
    Layout = null;
}
<h2>
    Ошибка сервера.
</h2>
<h3>@ViewBag.Type</h3>
<div>
  <span style="color:#f00">@ViewBag.Message</span>
  <pre style="overflow: auto; background-color: #FFFB97; color: #134D82; font-size: 11px;">@ViewBag.StackTrace</pre>
</div>

        public override ActionResult Default(ErrorDescription errorDescription)
        {
            ViewBag.Type = errorDescription.Exception.GetType();
            ViewBag.Message = errorDescription.Exception.Message;
            ViewBag.StackTrace = errorDescription.Exception.StackTrace;
            return this.View("Default");
        }


Спасибо. Погляжу как это можно исправить.

На MVC 5  не могу поставить. NuGet gишет что нет такого пакета

@ Alex: Может случайно сменили источник пакетов. Есть, вот ссылка на NuGet www.nuget.org/packages/Moveax.Mvc.ErrorHandler/

@ Михаил: В смысле надо вызвать 404 страницу ErrorController? Тогда RedirectToAction().

Добрый день. Как не пытался вызвать действие RedirectToAction(), попадает в Default с ошибкой 500, автор не могли бы вы написать как вызвать RedirectToAction() на примере. Ведь у вас в действии Http404 идет модель ErrorDescription и как не вызывай 404 он туда никак не попадает.

@ Eldar:
throw new HttpException(404, "Page not found");

Добрый вечер, доработки библиотеки не будет, как понимаю?) Довольно часто вместо страницы 404 отображается текст страницы, исходный код. Как у Михаила. Не перехватывает ошибку, если написать в контроллере return HttpNotFound(), return new HttpStatusCodeResult(HttpStatusCode.NotFound), то есть работает только для битых ссылок. Приходится извращаться(

Vusal А код отображается только на указанные 404? Или есть еще ситуации?

Здравствуйте! Пытаюсь сделать обработку неверной ссылки во view. Добавил в ErrorController вызов:

protected override void HandleUnknownAction(string actionName) {
            base.HandleUnknownAction(actionName);
        }

Но в параметре actionName приходит название View, где произошла ошибка, в моем случае это index. Без имени контроллера это мало что дает (в проекте десятки контроллеров и почти во всех есть index). В идеале хотелось бы получить как раз неправильную ссылку - имя контроллера и название. Пытался разобраться (добавил routes.MapRoute в RouteConfig) - пока не получилось. Буду признателен, если подскажете, можно ли с помощью Вашей библиотеки сделать такое.

Юрий В готовом виде (Controller + Action) нет. Но в  ErrorDescription есть свойство HttpRequest Request. В нем есть строка запроса. Зная как формируются пути в приложении, из нее можно попробовать получить нужную информацию.

Спасибо за ответ! В моем случае ErrorDescription почему-то приходит null-овый. Пока реализовал другим путем: большинство моих контроллеров наследуются от BaseController. Включил такую обработку в метод OnException. Туда приходит ExceptionContext. В нем есть имя контроллера и в текстовом описании ошибки - имя view.

Добавить комментарий