.NET Core Api – Catch exceptions using a middleware

In .NET Core, we are starting to get used to the fact that nothing comes for free, and in the world of ultimate freedom of choice, every feature needs to be implemented by us.

This includes error handling. In a previous post, .NET Core Catch Model Binding Exceptions, I explained how you can use ApiBehaviorOptions to catch exceptions thrown when someone POST the wrong JSON format to your endpoint.

It should be noted, that you can still catch exceptions in your endpoint using try…catch:

namespace MyCode.Controllers
{
  [ApiController]
  [Route("/api")]
  [Produces("application/json")]
  public class MyController : ControllerBase
  {
    [HttpPost()]
    public IActionResult LogDoNavigate([FromBody] MyModel model)
    {
      try
      {
	    ...
            ...
      }
      catch (Exception ex)
      {
        return base.BadRequest(new ProblemDetails() { Title = ex.Message, Detail = ex.ToString() });
      }
    }
  }
}

But what if the exception is not in this part of the Controller? What if the exception is in a System.Text.Serializion.JsonConverter that runs before your endpoint is called? The code will still throw an exception of course, but there is no immediate default handler, and the exception will not end up in your log file, or your Application Insights instance.

To handle such a scenario, you need to implement a Middleware. Middlewares are a piece of code that handles request and responses. You can use middlewares for many situations, but the most obvious are as exception handlers.

STEP 1: CREATE A CUSTOM MIDDLEWARE

This middleware will catch errors and return the exception as a JSON result object. You need to add your own logging or Application Insights code to write to log.

using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Security.Authentication;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace MyCode
{
  public class JsonExceptionMiddleware
  {
    public JsonExceptionMiddleware()
    {
    }

    public async Task Invoke(HttpContext context)
    {
      var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
      if (contextFeature != null && contextFeature.Error != null)
      {
        // ...
        // Add lines to your log file, or your 
        // Application insights instance here
        // ...
        context.Response.StatusCode = (int)GetErrorCode(contextFeature.Error);
        context.Response.ContentType = "application/json";

        await context.Response.WriteAsync(JsonConvert.SerializeObject(new ProblemDetails()
        {
          Status = context.Response.StatusCode,
          Title = contextFeature.Error.Message
        }));
      }
    }

    private static HttpStatusCode GetErrorCode(Exception e)
    {
      switch (e)
      {
        case ValidationException _:
          return HttpStatusCode.BadRequest;
        case FormatException _:
          return HttpStatusCode.BadRequest;
        case AuthenticationException _:
          return HttpStatusCode.Forbidden;
        case NotImplementedException _:
          return HttpStatusCode.NotImplemented;
        default:
          return HttpStatusCode.InternalServerError;
      }
    }
  }
}

STEP 2: ADD THE MIDDLEWARE TO YOUR IApplicationBuilder

Add the middleware to the Configure method in the Startup.cs file. Call the UseExceptionHandler method to use the middleware:

public void Configure(IApplicationBuilder app)
{
  ...
  ...
  app.UseExceptionHandler(new ExceptionHandlerOptions { 
    ExceptionHandler = new JsonExceptionMiddleware().Invoke 
    }
  );
  ...
  ...
}

You would expect the middleware to catch any exception. It does catch exceptions in your Controller code, so don’t need a try-catch handler in every Controller. But it does not catch model binding exceptions, so you still need to catch those.

MORE TO READ:

About briancaos

Developer at Pentia A/S since 2003. Have developed Web Applications using Sitecore Since Sitecore 4.1.
This entry was posted in .NET Core, c#, Microsoft Azure and tagged , , , . Bookmark the permalink.

3 Responses to .NET Core Api – Catch exceptions using a middleware

  1. This couldn’t be better timed for a .NET Core API I’m currently writing, thanks Brian!

  2. briancaos says:

    Thanks. Be sure to check out this article as well, if you wish to handle errors when someone POST the wrong JSON to your endpoint: https://briancaos.wordpress.com/2019/11/29/net-core-catch-model-binding-exceptions/

  3. Got them both bookmarked already ;)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.