全局异常过滤器或 Application_Error 都没有捕获未处理的异常
Neither the Global Exception Filter or the Application_Error are Catching Unhandled Exceptions
我有一个名为 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
全局过滤器,因为我意识到它正在将错误设置为处理,即使它是 运行。我不希望它无论如何都会执行,因为这个错误发生在控制器之外,但它可能会在以后咬我。
简短的回答是您正在添加 MVC 异常过滤器而不是 Web API 异常过滤器。
您的实施检查 ExceptionContext
而不是 HttpActionExecutedContext
public override void OnException(HttpActionExecutedContext actionExecutedContext)
由于框架将引发 Http 异常 而不是 MVC 异常,因此不会触发您的 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);
}
}
另一个重要的步骤是在 WebApiConfig.cs 中的 Register(HttpConfiguration config)
方法中注册您的全局 Web API 异常过滤器。
public static void Register(HttpConfiguration config)
{
...
config.Filters.Add(new CustomExceptionFilter());
}
Dave Alperovich 的回答将通过使用 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 }));
}
}
我有一个名为 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
全局过滤器,因为我意识到它正在将错误设置为处理,即使它是 运行。我不希望它无论如何都会执行,因为这个错误发生在控制器之外,但它可能会在以后咬我。
简短的回答是您正在添加 MVC 异常过滤器而不是 Web API 异常过滤器。
您的实施检查 ExceptionContext
而不是 HttpActionExecutedContext
public override void OnException(HttpActionExecutedContext actionExecutedContext)
由于框架将引发 Http 异常 而不是 MVC 异常,因此不会触发您的 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);
}
}
另一个重要的步骤是在 WebApiConfig.cs 中的 Register(HttpConfiguration config)
方法中注册您的全局 Web API 异常过滤器。
public static void Register(HttpConfiguration config)
{
...
config.Filters.Add(new CustomExceptionFilter());
}
Dave Alperovich 的回答将通过使用 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 }));
}
}