Как обработать все ошибки (перехваченные и неперехваченные) в ASP.Net MVC?

Есть большое приложение ASP.Net MVC, к-рое обращает к куче сервисов. Поведение сервисов не всегда стабильно, поэтому в коде множество блоков try/catch.
Есть необходимость дополнительно обрабатывать все exception: искать по БД и, в зависимости от результатов поиска, осуществлять дальнейшие действия.
Вопрос: как наложить обработку на все ошибки (перехваченные и неперехваченные) в ASP.Net MVC? Есть какой-то механизм или вписывать новый метод в каждый блок catch?
  • Вопрос задан
  • 1454 просмотра
Пригласить эксперта
Ответы на вопрос 2
andrewpianykh
@andrewpianykh
Я предпочитаю следующий подход:

1. Создаем контроллер ErrorController, например так:

public class ErrorController
{
	public virtual ActionResult BadRequest()
	{
		return View();
	}

	public virtual ActionResult Forbidden()
	{
		return View();
	}

	public virtual ActionResult Index()
	{
		return View();
	}

	public virtual ActionResult NotFound()
	{
		return View();
	}
}


2. Добавляем вьюхи

3. Создаем свой расширенный HandleErrorAttribute, например так:

public class ExtHandleErrorAttribute : HandleErrorAttribute
{
	//private readonly ILogger logger;

	public ExtHandleErrorAttribute(/*ILogger logger*/)
	{
		//this.logger = logger;
	}

	public override void OnException(ExceptionContext filterContext)
	{
		if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
		{
			return;
		}

		if (new HttpException(null, filterContext.Exception).GetHttpCode() != 500)
		{
			return;
		}

		if (!ExceptionType.IsInstanceOfType(filterContext.Exception))
		{
			return;
		}

		if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
		{
			filterContext.Result = new JsonResult
			{
				JsonRequestBehavior = JsonRequestBehavior.AllowGet,
				Data = new
				{
					error = true,
					message = filterContext.Exception.Message
				}
			};
		}
		else
		{
			var controllerName = (string)filterContext.RouteData.Values["controller"];
			var actionName = (string)filterContext.RouteData.Values["action"];
			var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);

			filterContext.Result = new ViewResult
			{
				ViewName = View,
				MasterName = Master,
				ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
				TempData = filterContext.Controller.TempData
			};
		}

		//var e = filterContext.Exception;
		//logger.Error(e, e.Message);

		filterContext.ExceptionHandled = true;
		filterContext.HttpContext.Response.Clear();
		filterContext.HttpContext.Response.StatusCode = 500;
		filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
	}
}


4. Регистрируем глобальный фильтр:
public class FilterConfig
{
	public static void RegisterGlobalFilters(GlobalFilterCollection filters)
	{
		var extHandleErrorAttribute = DependencyResolver.Current.GetService<ExtHandleErrorAttribute>();// new ExtHandleErrorAttribute();
		filters.Add(extHandleErrorAttribute);
	}
}


5. В Global.asax.cs добавляем метод Application_Error:

protected void Application_Error(object sender, EventArgs e)
{
	var httpContext = ((MvcApplication)sender).Context;

	var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
	var currentController = " ";
	var currentAction = " ";

	if (currentRouteData != null)
	{
		if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
		{
			currentController = currentRouteData.Values["controller"].ToString();
		}

		if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
		{
			currentAction = currentRouteData.Values["action"].ToString();
		}
	}

	var ex = Server.GetLastError();

	var controller = new ErrorController();
	var routeData = new RouteData();
	var action = "Index";

	if (ex is HttpException)
	{
		var httpEx = ex as HttpException;

		switch (httpEx.GetHttpCode())
		{
			case 404:
				action = "NotFound";
				break;

			case 403:
				action = "Forbidden";
				break;

			case 400:
				action = "BadRequest";
				break;

			default:
				action = "Index";
				//var logger = DependencyResolver.Current.GetService<ILogger>();
				//logger.Error(ex, ex.Message);
				break;
		}
	}

	httpContext.ClearError();
	httpContext.Response.Clear();
	httpContext.Response.StatusCode = ex is HttpException ? ((HttpException)ex).GetHttpCode() : 500;
	httpContext.Response.TrySkipIisCustomErrors = true;
	routeData.Values["controller"] = "Error";
	routeData.Values["action"] = action;

	controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);
	((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
}


6. В web.config добавляем в system.web
<customErrors mode="On" />

также в system.webServer можно добавить:

<httpErrors>
  <remove statusCode="400" subStatusCode="-1" />
  <remove statusCode="403" subStatusCode="-1" />
  <remove statusCode="502" subStatusCode="-1" />
  <remove statusCode="501" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <remove statusCode="404" subStatusCode="-1" />
  <error statusCode="400" prefixLanguageFilePath="" path="/Error/BadRequest" responseMode="ExecuteURL" />
  <error statusCode="404" prefixLanguageFilePath="" path="/Error/NotFound" responseMode="ExecuteURL" />
  <error statusCode="500" prefixLanguageFilePath="" path="/Error" responseMode="ExecuteURL" />
  <error statusCode="501" prefixLanguageFilePath="" path="/Error" responseMode="ExecuteURL" />
  <error statusCode="502" prefixLanguageFilePath="" path="/Error" responseMode="ExecuteURL" />
  <error statusCode="403" prefixLanguageFilePath="" path="/Error/Forbidden" responseMode="ExecuteURL" />
</httpErrors>
Ответ написан
Комментировать
ImmortalCAT
@ImmortalCAT
C# loving
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы