Handling exceptions globally with NET6

Microsoft dotnet .NET6

In this new post, I like to show how handling exceptions globally with NET6 adding a single class.

Generally, we handled all expected exceptions with try-catch wherever necessary in the code. When our code throws an exception, we need to map that exception. An exception is thrown in the lower layer like data access. You need to map that for all the layers until the presentation layer.

Or you have a bigger problem, losing the exception. Usually, you return a HTTP 500 status code, an internal error. But no one knows the error until checking the logging/tracing. Assuming you have logging/tracing.

So, having a global handling exception allows you to not be concerned about mapping every exception or mapping between layers.

public static class ExceptionMiddleware
{
    public static void ConfigureExceptionHandler(this IApplicationBuilder app, bool isDev)
    {
        app.UseExceptionHandler(appError =>
        {
            appError.Run(async context =>
            {
                context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                context.Response.ContentType = "application/json";

                var contextFeature = context.Features.Get<IExceptionHandlerPathFeature>();
                if (contextFeature != null)
                {
                    var ex = contextFeature?.Error;
                    await context.Response.WriteAsync(JsonConvert.SerializeObject(
                        new ProblemDetails
                        {

                            Type = ex.GetType().Name,
                            Status = (int)HttpStatusCode.InternalServerError,
                            Instance = contextFeature?.Path,
                            Title = isDev ? $"{ex.Message}" : "An error occurred.",
                            Detail = isDev ? ex.StackTrace : null
                        }));
                }
            });
        });
    }
}

The function ConfigureExceptionHandler receives the IApplicationBuilder and a variable isDev from the caller. In line 9, we return the HTTP status code 500, so the front end can be aware of the error.

If the environment is Development, the error message will have the StackTrace (line 24) and the exception message (line 23). But here, you can customize your error message. You also can use another object. You are not stuck with ProblemDetails if you want a different object with different fields and logic, you can do it. ProblemDetails is defined in Microsoft.AspNetCore.Http.Extensions

Register the ConfigureExceptionHandler

Now, we need to register the middleware goes to the Program.cs or Startup.cs and type this.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, 
                      IApiVersionDescriptionProvider provider)
{
    // ...
    Middlewares(app, env);
}

void Middlewares(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.ConfigureExceptionHandler(env.IsDevelopment());
}

After that, if you run your API in development, in case of an internal server error, you get a Json like the following image.

Error in the web api in the development environment - Handling exceptions globally with .NET6
Error in the web api in the development environment

So, the same code in production returns the following Json.

Error in the web api in the production environment - Handling exceptions globally with .NET6
Error in the web api in the production environment

Wrap up

In conclusion this is how handling exceptions globally with NET6. Please leave your comment below.

Happy new year!

Leave a Reply

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