Ни глобальный фильтр исключений, ни приложение Application_Error не вызывают необработанные исключения
У меня есть глобальный фильтр исключений с именем LogErrorAttribute
:
public class LogErrorAttribute : IExceptionFilter
{
private ILogUtils logUtils;
public void OnException(ExceptionContext filterContext)
{
if (this.logUtils == null)
{
this.logUtils = StructureMapConfig.Container.GetInstance<ILogUtils>();
}
this.logUtils.LogError(HttpContext.Current.User.Identity.GetUserId(), "Unknown error.", filterContext.Exception);
}
}
Он зарегистрирован вместе со стандартным фильтром HandleErrorAttribute
:
filters.Add(new LogErrorAttribute());
filters.Add(new HandleErrorAttribute());
Я регистрирую эти фильтры следующим образом:
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
У меня также есть резерв Application_Error
:
protected void Application_Error()
{
var exception = Server.GetLastError();
Server.ClearError();
var httpException = exception as HttpException;
//Logging goes here
var routeData = new RouteData();
routeData.Values["controller"] = "Error";
routeData.Values["action"] = "Index";
if (httpException != null)
{
if (httpException.GetHttpCode() == 404)
{
routeData.Values["action"] = "NotFound";
}
Response.StatusCode = httpException.GetHttpCode();
}
else
{
Response.StatusCode = 500;
}
// Avoid IIS7 getting involved
Response.TrySkipIisCustomErrors = true;
// Execute the error controller
if (exception != null)
{
this.errorLogger.Log(LogLevel.Error, "An unknown exception has occurred.", exception);
}
else if (httpException != null)
{
this.errorLogger.Log(LogLevel.Error, "An unknown HTTP exception has occurred.", httpException);
}
else
{
this.errorLogger.Log(LogLevel.Error, "An unknown error has occurred.");
}
}
Теперь у меня есть контроллер API, который захватывает некоторые данные из базы данных, а затем использует AutoMapper
для сопоставления моделей для просмотра моделей:
var viewModels = AutoMapper.Mapper.Map(users, new List<UserViewModel>());
Внутри этой конфигурации AutoMapper
пользовательский резольвер выполняет одно из свойств:
var appModuleAssignments = this.appModuleAssignmentManager.Get(userId);
var appModules = appModuleAssignments.Select(x => this.appModuleManager.Get(x.AppModuleId));
return AutoMapper.Mapper.Map(appModules, new List<AppModuleViewModel>());
В настоящий момент я заставляю оператор appModuleManager.Get
генерировать регулярное исключение:
throw new Exception("Testing global filter.");
Это впоследствии выдает исключение в AutoMapper
, оба из которых необработаны, однако ни глобальный фильтр, ни Application_Error
не собирают это исключение.
Что я сделал здесь неправильно?
Несколько вещей, которые я сделал с момента публикации:
- Добавлен атрибут
customErrors
в Web.config
, чтобы включить их on
.
- Удален глобальный фильтр
HandleErrorAttribute
, потому что я понял, что он установил ошибку для обработки, если она была даже запущена. Я бы не ожидал, что он будет выполняться в любом случае, потому что эта ошибка происходит за пределами контроллера, но, скорее всего, это меня убьет позже.
Ответы
Ответ 1
Короткий ответ заключается в том, что вы добавляете MVC Исключительный фильтр, а не Веб-API Исключительный фильтр.
Ваша реализация проверяет ExceptionContext
, а не HttpActionExecutedContext
public override void OnException(HttpActionExecutedContext actionExecutedContext)
Так как структура создаст Http Exception, а не MVC Exception, ваш метод переопределения OnException
не будет запущен.
Итак, более полный пример:
public class CustomExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
message = "Web API Error";
status = HttpStatusCode.InternalServerError;
actionExecutedContext.Response = new HttpResponseMessage()
{
Content = new StringContent(message, System.Text.Encoding.UTF8, "text/plain"),
StatusCode = status
};
base.OnException(actionExecutedContext);
}
}
Еще один важный шаг - зарегистрировать свой фильтр исключения глобальных веб-API в WebApiConfig.cs в методе Register(HttpConfiguration config)
.
public static void Register(HttpConfiguration config)
{
...
config.Filters.Add(new CustomExceptionFilter());
}
Ответ 2
Ответ Дэйва Альперовича решит вашу проблему, используя HttpActionExecutedContext
public override void OnException(HttpActionExecutedContext context)
Однако, поскольку вы пытаетесь захватить все возможные исключения, которые может создать ваше приложение, кроме, кроме фильтров исключений, следует использовать обработчики сообщений. Подробное объяснение можно найти здесь - http://www.asp.net/web-api/overview/error-handling/web-api-global-error-handling.
В целом, существует ряд случаев, когда обработчики исключений не могут обрабатываться. Например:
- Исключения, выведенные из конструкторов контроллера.
- Исключения, выведенные из обработчиков сообщений.
- Исключения, возникающие при маршрутизации.
- Исключения, возникающие при сериализации содержимого ответа
Итак, Если необработанная ошибка возникает из любого места приложения, ваш обработчик исключений поймает его и позволит вам предпринять конкретные действия.
//Global exception handler that will be used to catch any error
public class MyExceptionHandler : ExceptionHandler
{
private class ErrorInformation
{
public string Message { get; set; }
public DateTime ErrorDate { get; set; }
}
public override void Handle(ExceptionHandlerContext context)
{
context.Result = new ResponseMessageResult(context.Request.CreateResponse(HttpStatusCode.InternalServerError,
new ErrorInformation { Message="An unexpected error occured. Please try again later.", ErrorDate=DateTime.UtcNow }));
}
}