Take advantage of the new IExceptionHandler interface to handle exceptions gracefully in your ASP.NET Core applications. Credit: Florent Darrault Microsoft’s November release of .NET 8 brought all kinds of great new features. One of the nice improvements introduced in ASP.NET Core 8 is IExceptionHandler, an interface that makes it easier to handle exceptions gracefully in ASP.NET Core web applications. Error handling has a long history in programming languages and frameworks. IExceptionHandler simplifies error handling by providing a callback and a central location for handling known exceptions. In this article we’ll discuss how you can use IExceptionHandler in your ASP.NET Core 8 applications and present meaningful error responses to the user. To use the code examples provided in this article, you should have Visual Studio 2022 installed in your system. If you don’t already have a copy, you can download Visual Studio 2022 here. Create an ASP.NET Core Web API project in Visual Studio 2022 To create an ASP.NET Core 8 Web API project in Visual Studio 2022, follow the steps outlined below. Launch the Visual Studio 2022 IDE. Click on “Create new project.” In the “Create new project” window, select “ASP.NET Core Web API” from the list of templates displayed. Click Next. In the “Configure your new project” window, specify the name and location for the new project. Optionally check the “Place solution and project in the same directory” check box, depending on your preferences. Click Next. In the “Additional Information” window shown next, select “.NET 8.0 (Long Term Support)” as the framework version, and ensure that the “Use controllers” box is unchecked. We’ll be using minimal APIs in this project. Elsewhere in the “Additional Information” window, leave the “Authentication Type” set to “None” (the default) and make sure the check boxes “Enable Open API Support,” “Configure for HTTPS,” and “Enable Docker” remain unchecked. We won’t be using any of those features here. Click Create. We’ll use this ASP.NET Core Web API project to work with the IExceptionHandler interface in the sections below. Why do we need an exception handler? In ASP.NET Core, an exception handler is a component that can handle exceptions globally in an application. It can catch all unhandled exceptions and then generate appropriate error responses. An exception handler can help implement a centralized error-handling mechanism, allowing your applications to fail gracefully. This will enable you to ensure that all exceptions are handled, errors are correctly logged and processed, and meaningful error responses are generated and presented to the user. Let us understand this with an example. Consider the following code. 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(opt => { }); app.MapGet("/GenerateError", () => { throw new NotImplementedException(); }); app.Run(); When you execute the application and hit the /GenerateError endpoint, the response displayed in your web browser will appear as shown below. IDG Figure 1: Response generated without using exception handlers. Note that the error response is not formatted and it is quite difficult to read and understand the error metadata from this response. Introducing the IExceptionHandler interface Exception handling has been used in programming languages for decades to handle run-time errors in applications. ASP.NET Core 8 improves exception handling significantly with the introduction of the IExceptionHandler interface. You can create a central class for exception handling in ASP.NET Core 8 by implementing the IExceptionHandler interface. The IExceptionHandler interface looks like this: public interface IExceptionHandler { ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken); } The IExceptionHandler interface contains the declaration of the TryHandleAsync method. This method accepts three parameters: an instance of type HttpContext, an Exception, and a CancellationToken. It returns ValueTask to indicate if the exception was handled successfully or not. When you implement the TryHandleAsync method in your class that extends the IExceptionHandler interface, you must return a boolean value from this method. You should return true if the exception was handled or false otherwise. Create a custom exception handler in ASP.NET Core To implement the IExceptionHandler interface, create a new class named GlobalExceptionHandler in a file of the same name with a .cs extension. And enter the following code in there. 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 reside in the TryHandleAsync method. The following code snippet shows how you can handle exceptions that occur in your application asynchronously. 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; } We’ll now take advantage of the open source ProblemDetails middleware to generate consistent, structured, machine-readable error messages. The CreateProblemDetails method returns an instance of ProblemDetails containing the error metadata, as shown in the code snippet given below. private ProblemDetails CreateProblemDetails(in HttpContext httpContext, in Exception exception) { httpContext.Response.ContentType = “application/json”; switch (exception) { case NotImplementedException notImplementedException: httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; break; default: httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; break; } return new ProblemDetails { Status = (int)httpContext.Response.StatusCode, Type = exception.GetType().Name, Title = “An unexpected error occurred”, Detail = exception.Message, Instance = $”{httpContext.Request.Method} {httpContext.Request.Path}” }; } Complete source code of our custom exception handler The following code listing comprises the complete source code of the GlobalExceptionHandler class. public class GlobalExceptionHandler(IHostEnvironment hostEnvironment, ILogger<GlobalExceptionHandler> logger) : IExceptionHandler { 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; } private ProblemDetails CreateProblemDetails(in HttpContext httpContext, in Exception exception) { httpContext.Response.ContentType = “application/json”; switch (exception) { case NotImplementedException notImplementedException: httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; break; default: httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; break; } return new ProblemDetails { Status = (int)httpContext.Response.StatusCode, Type = exception.GetType().Name, Title = “An unexpected error occurred”, Detail = exception.Message, Instance = $”{httpContext.Request.Method} {httpContext.Request.Path}” }; } } Register the exception handler with the request processing pipeline Now that our custom exception handler is ready, we need to register it with the request processing pipeline to start using it in our application. You can register the exception handler by including the following line of code in the Program.cs file. builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); Let us now create an error handling endpoint in Program.cs. When invoked this endpoint will display a user-friendly error response as specified in the configured error handler. To create an error handling endpoint, enter the following code in the Program.cs file. app.MapGet("/GenerateError", () => { throw new ValidationException(); }); The call to the UseExceptionHandler() method configures the request processing pipeline in ASP.NET Core to handle exceptions using our middleware. app.UseExceptionHandler(opt => { }); Now when you execute the application and hit the /GenerateError endpoint, the response displayed in the web browser will appear as below. Much better! IDG Figure 2: Error response generated using an error handler. It is always advisable to implement your own custom exception handling in your application. This ensures not only that the exception handling logic meets your requirements, but also that the error responses are generated in your desired format. Naturally, rolling your own exception handler also gives you much more granular control over the exception handling mechanism. Related content news Wasmer WebAssembly platform now backs iOS Wasmer 5.0 release also features improved performance, a leaner codebase, and discontinued support for the Emscripten toolchain. By Paul Krill Oct 30, 2024 2 mins Mobile Development Web Development Software Development news analysis What Entrust certificate distrust means for developers Secure communications between web browsers and web servers depend on digital certificates backed by certificate authorities. What if the web browsers stop trusting your CA? By Travis Van Oct 30, 2024 9 mins Browser Security Web Development Application Security news Next.js 15 arrives with faster bundler High-performance Rust-based Turbopack bundler moves from beta to stable with the latest update of the React-based web framework. By Paul Krill Oct 24, 2024 2 mins JavaScript React Web Development feature WasmGC and the future of front-end Java development WebAssembly’s garbage collection extension makes it easier to run languages like Java on the front end. Could it be the start of a new era in web development? By Matthew Tyson Oct 16, 2024 10 mins Web Development Software Development Resources Videos