全局异常过滤器或 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 都没有发现这个异常。

我做错了什么?


发帖后我做了几件事:

  1. customErrors 属性添加到 Web.config 以将它们变为 on
  2. 删除了 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 }));
        }
   }