全局异常过滤器或Application_Error都不是捕获未处理的异常

时间:2016-02-13 16:53:04

标签: c# asp.net-mvc asp.net-mvc-4 exception-handling automapper

我有一个名为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全局过滤器,因为我意识到它正在设置错误,如果它甚至正在运行则要处理。我不希望它反正执行,因为这个错误发生在控制器外部,但它可能会让我稍后咬一下。

2 个答案:

答案 0 :(得分:9)

简短的回答是,您要添加 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);
    }
}

另一个重要步骤是在Register(HttpConfiguration config)方法中在WebApiConfig.cs中注册Global Web API异常过滤器。

public static void Register(HttpConfiguration config)
{

...

config.Filters.Add(new CustomExceptionFilter());

}

答案 1 :(得分:0)

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 }));
        }
   }