0

I created a global Exception handler middelware to catch all my custom exceptions.

When throwing an Exception in my DAL I expect that the middelware will catch it as the same type that it was thrown.

// API
[HttpGet]
[Route("api/users")]
public IActionResult Get(int id)
{
    var user = _userService.GetById(id);
    return Ok(user);
}

// Repository
public async Task<List<User>> GetById(int id)
{
    throw new EntityNotFoundException("code", "message");
    // .. return user 
}

// Exception handler
public async Task Invoke(HttpContext httpContext)
{
    try
    {
        await _next(httpContext);
    }
    catch (Exception ex) // ex is of type JsonSerializationException 
    {
        if (ex is EntityNotFoundException)
        {
            // Handle exception
        }
    }
}

In the above example the Exception is handled but is of type JsonSerializationException with an InnerException of type System.AggregateException that contains another InnerException with type EntityNotFoundException.

It seems that the Exception gets nested for each layer it gets passed along (DAL > Service > API). How can I avoid this so that I can catch the Exception as the original type?

Sam
  • 1,303
  • 3
  • 23
  • 41
  • Can you please provide more code about what kind of handler that is? Doesn't seem to implement the IExceptionFilter interface, does it (looks like an action-filter)? Also did understand you correctly that DAL throws an exception then Service throws an exception too? – alsami Mar 12 '18 at 18:43
  • Only the DAL is throwing an exception. The Exception Handler does not need to implement IExceptionFilter as far as I can tell from other examples: https://stackoverflow.com/questions/38630076/asp-net-core-web-api-exception-handling (look accepted answer) – Sam Mar 12 '18 at 18:47
  • Yeah, you could also implement the filter attribute but you are missing the function OnException in your filter. Isn't your implementation an action filter? – alsami Mar 12 '18 at 18:48
  • It is not a filter, it is a custom middelware. Will using a filter be able to catch the exception as the type that it was thrown or will it not make a difference? – Sam Mar 12 '18 at 18:52
  • Added an answer to your question, hope it helps. – alsami Mar 12 '18 at 19:07
  • Thanks, I will give it a try ASAP and let you know what the result is. – Sam Mar 12 '18 at 19:08
  • So about the JsonSerializationException to tell you more about why its happening we should see the stacktrace. The AggregateException is the way of the async/await - Task Library to bubble up the exceptions that happened in their scope. – Nick Polyderopoulos Mar 12 '18 at 23:17

1 Answers1

2

The example you provided looks good but it lacks one important thing, which is single responsibility.

ASP.NET Core has a better approach, which is using exception filters, that can be registered globally too and can be written for each custom-exception and even for unhandled exceptions.

Sample:

public class EntityNotFoundExceptionFilter : IExceptionFilter
{
   public EntityNotFoundExceptionFilter(// some dependencies that u want to inject)
   {
       ...
   }

   public void OnException(ExceptionContext context)
   {
      if (!(context.Exception is EntityNotFoundException))
      {
          return;
      }

      context.ExceptionHandled = true;
      context.Result = new NotFoundObjectResult // will produce 404 response, you can also set context.HttpContext.Response.StatusCode based on your exceptions statuscode and return an ObjectResult instead
      {
         context.Exception.Message
      }
   }
}

Now in your Startup.cs in the ConfigureServices(...) function add the following

public void ConfigureService(IServiceCollection services)
{
    ...
    services.AddMvc(options => 
    {
        ...
        options.Filters.Add(typeof(EntityNotFoundExceptionFilter));
        ...
    }
    ...
}

You will end up writing many filters but it is a cleaner approach and that is how the asp.net-core filterpipeline should be used + this will be working :)

I am not 100% sure why there are so many exceptions in your current implementation but my guess is that asp.net tries to return the exception and then fails to serialize it and stuff like that.

Edit:

I create a minimal example that can be found here. Just access the url via http://localhost:58741/api/some after cloning the project.

alsami
  • 8,996
  • 3
  • 25
  • 36
  • I tried using a Filter but it didn't work. The OnException method only catches the exception when it is thrown in a Controller. When thrown in an another layer like the DAL it results again in a Newtonsoft.Json.JsonSerializationException (without being catched in the ExceptionFilter). – Sam Mar 15 '18 at 19:45
  • There must be smth causing more errors. I would suggest you to check the call stack and see what is throwing more exceptions. Gimme a second producing a minimal example that you can use and see what is causing your problem and why it is not working. – alsami Mar 15 '18 at 19:53
  • Updated the post with an example you can download and run. If you need more help please provide your Startup-Class and potential Middleware too. If you don't mind host the whole project on git, I will take a look and see what is causing the problem. – alsami Mar 15 '18 at 20:08
  • It worked! Sorry for the inconvenience, it was a asynchronous problem. – Sam Mar 15 '18 at 20:09