Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Neither the Global Exception Filter or the Application_Error are Catching Unhandled Exceptions

I have a global exception filter named 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);
    }
}

It's registered along with the standard HandleErrorAttribute filter:

filters.Add(new LogErrorAttribute());
filters.Add(new HandleErrorAttribute());

I'm registering those filters like this:

FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

I also have an Application_Error fallback:

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

Now, I have an API controller that grabs some data from the database and then uses AutoMapper to map the models to view models:

var viewModels = AutoMapper.Mapper.Map(users, new List<UserViewModel>());

Inside that AutoMapper configuration a custom resolver executes for one of the properties:

var appModuleAssignments = this.appModuleAssignmentManager.Get(userId);
var appModules = appModuleAssignments.Select(x => this.appModuleManager.Get(x.AppModuleId));
return AutoMapper.Mapper.Map(appModules, new List<AppModuleViewModel>());

At the moment I'm forcing the appModuleManager.Get statement to throw a regular exception:

throw new Exception("Testing global filter.");

This subsequently throws an exception in AutoMapper, both of which are unhandled, however neither the global filter or the Application_Error are picking up this exception.

What did I do wrong here?


A couple things I have done since posting:

  1. Added the customErrors attribute to the Web.config to turn them on.
  2. Removed the HandleErrorAttribute global filter because I realized it was setting the error to handled if it were even running. I would not expect it to be executing anyway because this error occurs outside the controller, but it would have likely bit me later.
like image 837
Mike Perrenoud Avatar asked Feb 13 '16 16:02

Mike Perrenoud


People also ask

How do you handle unhandled exception?

The . NET Framework provides a couple events that can be used to catch unhandled exceptions. You only need to register for these events once in your code when your application starts up. For ASP.NET, you would do this in the Startup class or Global.

How do I register an exception filter globally in Web API?

To apply the filter globally to all Web API controllers, add an instance of the filter to the GlobalConfiguration. Configuration. Filters collection. Exception filters in this collection apply to any Web API controller action.

How exceptions are handled globally in C#?

An ExceptionFilterAttribute is used to collect unhandled exceptions. You can register it as a global filter, and it will function as a global exception handler. Another option is to use a custom middleware designed to do nothing but catch unhandled exceptions.


1 Answers

The short answer is that you are adding a MVC Exception Filter rather than a Web API Exception Filter.

Your implementation checks for ExceptionContext rather than HttpActionExecutedContext

public override void OnException(HttpActionExecutedContext actionExecutedContext)

Since the framework will raises a Http Exception rather than a MVC Exception, your OnException override method is not triggered.

So, a more complete example:

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

Another important step is to register your Global Web API Exception Filter in WebApiConfig.cs, in the Register(HttpConfiguration config) method.

public static void Register(HttpConfiguration config)
{

...

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

}
like image 180
Dave Alperovich Avatar answered Oct 18 '22 22:10

Dave Alperovich