How to Create an exception handler in ASP.NET Core 8
By FoxLearn 1/2/2025 9:55:51 AM 40
The article explains how to leverage IExceptionHandler to provide users with meaningful error responses, enhancing the overall error-handling experience in ASP.NET Core 8 applications.
Understanding the Need for an Exception Handler
In ASP.NET Core, an exception handler is a component that manages unhandled exceptions globally, providing a centralized mechanism for error handling. It ensures that exceptions are caught, errors are logged and processed, and meaningful error responses are generated, allowing the application to fail gracefully.
using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Mvc; using System.ComponentModel.DataAnnotations; using System.Net; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.UseExceptionHandler(option => { }); app.MapGet("/GenerateError", () => { throw new NotImplementedException(); }); app.Run();
When accessing the /GenerateError endpoint, the error response displayed in the browser is unformatted, making it difficult to read and understand the error metadata.
Introducing the IExceptionHandler interface
ASP.NET Core 8 enhances this with the introduction of the IExceptionHandler interface, allowing developers to create a centralized class for handling exceptions in their applications.
public interface IExceptionHandler { ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken); }
The IExceptionHandler interface includes the TryHandleAsync method, which takes three parameters: HttpContext, Exception, and CancellationToken, and returns a ValueTask to indicate whether the exception was handled. When implementing this method, you must return true if the exception is handled, or false if not.
How to Create a custom exception handler in ASP.NET Core
To implement the IExceptionHandler interface, create a class called GlobalExceptionHandler in a file named GlobalExceptionHandler.cs and add the appropriate code to it.
public class GlobalExceptionHandler(IHostEnvironment hostEnvironment, ILogger<GlobalExceptionHandler> logger) : IExceptionHandler { public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { return true; } }
Your custom error handling logic should be placed in the TryHandleAsync method, where you can handle exceptions asynchronously, as shown in the following code snippet.
private const string ExceptionMessage = "An unhandled exception has occurred while executing the request."; public async ValueTask TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { logger.LogError(exception, exception is Exception ? exception.Message : ExceptionMessage); var problemDetails = CreateProblemDetails(httpContext, exception); await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken); return true; }
The CreateProblemDetails method creates a ProblemDetails object containing error metadata, as shown below:
private ProblemDetails CreateProblemDetails(HttpContext httpContext, Exception exception) { httpContext.Response.ContentType = "application/json"; // Determine the appropriate HTTP status code based on the exception type switch (exception) { case UnauthorizedAccessException unauthorizedAccessException: httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; break; case ArgumentNullException argumentNullException: httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; break; default: httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; break; } // Create a ProblemDetails object with error metadata return new ProblemDetails { Status = httpContext.Response.StatusCode, Type = exception.GetType().Name, Title = "An error occurred processing your request", Detail = exception.Message, Instance = $"{httpContext.Request.Method} {httpContext.Request.Path}" }; }
Full Source Code for Custom Exception Handler
The following code provides the complete source code for the GlobalExceptionHandler class.
public class GlobalExceptionHandler : IExceptionHandler { private const string ExceptionMessage = "An unhandled exception has occurred while executing the request."; private readonly ILogger<GlobalExceptionHandler> _logger; public GlobalExceptionHandler(IHostEnvironment hostEnvironment, ILogger<GlobalExceptionHandler> logger) { _logger = logger; } public async ValueTask TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { // Log the exception _logger.LogError(exception, exception.Message); // Create the problem details response var problemDetails = CreateProblemDetails(httpContext, exception); // Write the response as JSON await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken); return true; } private ProblemDetails CreateProblemDetails(HttpContext httpContext, Exception exception) { httpContext.Response.ContentType = "application/json"; // Set status code based on exception type httpContext.Response.StatusCode = exception switch { NotImplementedException => (int)HttpStatusCode.BadRequest, _ => (int)HttpStatusCode.InternalServerError }; // Return structured problem details return new ProblemDetails { Status = httpContext.Response.StatusCode, Type = exception.GetType().Name, Title = "An unexpected error occurred", Detail = exception.Message, Instance = $"{httpContext.Request.Method} {httpContext.Request.Path}" }; } }
Registering the Exception Handler in the Pipeline
To use the custom exception handler in your application, register it in the request processing pipeline by adding builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
in the Program.cs file.
Then, create an error-handling endpoint using app.MapGet("/GenerateError", () => { throw new ValidationException(); });
to simulate an error.
Next, configure the pipeline to use the exception handler with app.UseExceptionHandler(opt => { });
. When accessing the /GenerateError
endpoint, the application will display a user-friendly error response, as defined by your custom handler.
For example:
builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); app.MapGet("/GenerateError", () => { throw new ValidationException(); }); app.UseExceptionHandler(option => { });
Implementing a custom exception handler ensures that error responses meet your specific format and requirements, giving you more control over how exceptions are managed in your application.
- How to use Brotli for response compression in ASP.NET Core
- How to use SignalR in ASP.NET Core
- How to use the Dapper ORM in ASP.NET Core
- How to enable CORS in ASP.NET Core
- How to implement HTTP.sys web server in ASP.NET Core
- How to use File Providers in ASP.NET Core
- How to use policy-based authorization in ASP.NET Core
- How to enforce SSL in ASP.NET Core