HomeNikola Knezevic
Banner

Global Exception Handling in ASP.NET Core

09 Apr 2025
5 min

Sponsor Newsletter

Regardless of your approach to flow control, exceptions will occur.

They can be minimized, but never fully avoided.

You can use the Result Pattern to effectively handle expected failures. However, sooner or later, unexpected exceptions will occur, and your codebase must be ready to handle them.

The best approach to manage exceptions is to implement a global exception handling mechanism.

This ensures your application fails gracefully, logs meaningful information, and provides consistent responses to the caller.

Handling Exceptions with Middleware

In case you are working on versions prior to .NET 8, the best way to handle exceptions is by using middleware.

If you haven’t worked with middleware before, take a look at this blog post: Middlewares

By using middleware, you can centralize your exception handling logic, making it easier to manage and maintain:

csharp
public class GlobalExceptionMiddleware(ILogger logger) : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        try
        {
            await next(context);
        }
        catch (Exception exception)
        {
            logger.LogError(exception, "{Message}", exception.Message);

            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            await context.Response.WriteAsJsonAsync(new ProblemDetails
            {
                Status = StatusCodes.Status500InternalServerError,
                Title = "Server Error",
            });
        }
    }
}

In this example, I've created a factory-based middleware that handles exceptions globally within the application.

It catches any unhandled exceptions during request processing, logs the error details, and ensures the client receives a consistent response.

The middleware sets the response status code to 500 and returns a ProblemDetails object with a descriptive message.

You could return any response, but the best practice is to return a ProblemDetails object.

ProblemDetails standard is defined by RFC 7807 and provides a consistent and structured way to represent error information in HTTP responses.

IExceptionHandler in .NET 8

With the release of .NET 8, we can now utilize the IExceptionHandler interface to create a global exception handler.

IExceptionHandler is an interface that gives the developer a callback for handling exceptions in a central location:

csharp
public class GlobalExceptionHandler(ILogger logger) : IExceptionHandler
{
    public async ValueTask TryHandleAsync(
        HttpContext httpContext,
        Exception exception,
        CancellationToken cancellationToken)
    {
        logger.LogError(exception, "{Message}", exception.Message);

        httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;

        await httpContext.Response.WriteAsJsonAsync(new ProblemDetails
        {
            Status = StatusCodes.Status500InternalServerError,
            Title = "Server Error"
        }, cancellationToken);

        return true;
    }
}

IExceptionHandler implementations are registered by calling AddExceptionHandler<T> and added to the pipeline by calling UseExceptionHandler methods:

csharp
builder.Services.AddExceptionHandler();

// ...

app.UseExceptionHandler();

The lifetime of an IExceptionHandler instance is singleton and you can add multiple implementations.

NOTE: If you have multiple implementations they will be called in the order they have been registered.

You’ll notice that this implementation returns a bool. That’s because if an exception handler successfully handles the exception, it should return true to stop further processing.

If it returns false, the framework will attempt to pass the exception to another registered handler.

If none of the handlers can handle the exception, control falls back to the default error-handling behavior.

Conclusion

By centralizing your exception handling, you not only improve maintainability and observability but also provide a better experience for your users through consistent and meaningful error responses.

With middlewares and the new IExceptionHandler interface introduced in .NET 8, you have powerful tools to ensure your application handles exceptions consistently and gracefully.

Implementing a global exception handling mechanism isn't just good practice, it's essential when building applications.

If you want to check out examples I created, you can find the source code here:

Source Code

I hope you enjoyed it, subscribe and get a notification when a new blog is up!

Subscribe

Stay tuned for valuable insights every Thursday morning.