Всё мы знаем, что в ASP.NET MVC есть такой атрибут
Но нигде, в том же MSDN не сказано (ткните меня носом дайте ссылку где это написано, если я просмотрел), что он обрабатывает только исключения, устанавливающие код ответа сервера в 500.
Посмотрев на исходный код
Не знаю, как вам, а мне удобнее при возникновении исключения, чтобы пользователи видели специальную страницу для этого, а не «жёлтую страницу смерти» или вообще как браузер отображает стандартную для него страницу с кодом ответа сервера (зависит от настроек в Web.config, но об этом позже).
Создание собственного аналога
Итак, мы выяснили, что стандартный
Можно конечно, создать класс наследуя его от интерфейса
Для начала скажу, что это код стандартного
Собственно, что он делает:
Класс наш готов, теперь заменим стандартный атрибут на наш. Сделаем это через глобальные фильтры, чтобы нам не писать его каждому контроллеру или действию контроллера. В данном случае это не критично, но в реальности может пригодиться. Итак, идём в Global.asax.cs и прописываем вместо стандартного фильтра, наш только что созданный атрибут:
Теперь, чтобы убедится, что наш фильтр работает и передаёт необходимые данные во
И добавим в Web.config (который находится в корне) в секцию
Этой строчкой мы включаем пользовательскую обработку ошибок, точнее пользовательские страницы для ошибок.
Теперь нам необходимо вызвать исключение, я сделаю это в действии стандартного контроллера (созданного студией):
Всё! Запускаем проект, переходим в раздел «About» и видим, что наш атрибут отработал корректно.
HandleErrorAttribute
, который как сказано в MSDN Представляет атрибут, используемый для обработки исключения, вызываемого методом действия.
Но нигде, в том же MSDN не сказано (
Посмотрев на исходный код
HandleErrorAttribute
легко убедиться в этом. Там имеются следующие строки:// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
// ignore it.
if (new HttpException(null, exception).GetHttpCode() != 500) {
return;
}
Не знаю, как вам, а мне удобнее при возникновении исключения, чтобы пользователи видели специальную страницу для этого, а не «жёлтую страницу смерти» или вообще как браузер отображает стандартную для него страницу с кодом ответа сервера (зависит от настроек в Web.config, но об этом позже).
Создание собственного аналога HandleErrorAttribute
Итак, мы выяснили, что стандартный
HandleErrorAttribute
нам не подходит, ну что же, создадим свой.Можно конечно, создать класс наследуя его от интерфейса
IExceptionFilter
, но нас в общем то устраивает поведение стандартного HandleErrorAttribute
, если бы он обрабатывал все исключения. А раз нас почти всё устраивает, будет наследовать наш класс от неугодного нам HandleErrorAttribute
.public class HandleAllErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
// If custom errors are disabled, we need to let the normal ASP.NET exception handler
// execute so that the user can see useful debugging information.
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
{
return;
}
Exception exception = filterContext.Exception;
if (!ExceptionType.IsInstanceOfType(exception))
{
return;
}
string controllerName = (string)filterContext.RouteData.Values["controller"];
string actionName = (string)filterContext.RouteData.Values["action"];
HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
filterContext.Result = new ViewResult
{
ViewName = View,
MasterName = Master,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = new HttpException(null, exception).GetHttpCode();
// Certain versions of IIS will sometimes use their own error page when
// they detect a server error. Setting this property indicates that we
// want it to try to render ASP.NET MVC's error page instead.
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
Для начала скажу, что это код стандартного
HandleErrorAttribute
, за исключением нескольких строк, проверяющих на HttpStatusCode
.Собственно, что он делает:
- Проверяет входные параметры на валидность
- Проверяет включены ли
customErrors
- Заполняет данными класс
HandleErrorInfo
- Создаёт новый
ViewResult
, заполняет данными и присваивает вместо текущего - Очищает ошибки сервера и устанавливает код ответа сервера
Класс наш готов, теперь заменим стандартный атрибут на наш. Сделаем это через глобальные фильтры, чтобы нам не писать его каждому контроллеру или действию контроллера. В данном случае это не критично, но в реальности может пригодиться. Итак, идём в Global.asax.cs и прописываем вместо стандартного фильтра, наш только что созданный атрибут:
filters.Add(new HandleAllErrorAttribute());
Испытание
Теперь, чтобы убедится, что наш фильтр работает и передаёт необходимые данные во
View
, несколько изменим стандартный ~/Views/Shared/Error.cshtml:@model System.Web.Mvc.HandleErrorInfo
@{
ViewBag.Title = "Error";
}
<h2>
Sorry, an error occurred while processing your request.
</h2>
<h3>@Model.Exception.Message</h3>
И добавим в Web.config (который находится в корне) в секцию
System.Web
следующую строку:<customErrors mode="On" defaultRedirect="Error" />
Этой строчкой мы включаем пользовательскую обработку ошибок, точнее пользовательские страницы для ошибок.
Теперь нам необходимо вызвать исключение, я сделаю это в действии стандартного контроллера (созданного студией):
public ActionResult About()
{
throw new HttpException(403, "Доступ запрещён!");
return View();
}
Всё! Запускаем проект, переходим в раздел «About» и видим, что наш атрибут отработал корректно.