- Home
- .NET tutorials
- Exception handling has its own middleware in .NET 8
Exception handling has its own middleware in .NET 8
Published: Monday 15 January 2024
We used to have to write our own custom middleware to handle exception handling in C#.
But with .NET 8, there is a major update that dramatically improves the way we handle errors.
Without any error handling
In an ASP.NET Core Web API, we can set up a basic controller with an endpoint that throws an exception.
// ErrorController.cs
[Route("api/[controller]")]
[ApiController]
public class ErrorController : ControllerBase
{
[HttpGet]
public IActionResult Index()
{
throw new Exception("This is not going to work");
}
}
When we run the endpoint, the exception is thrown and we have the debug information so we help resolve the error.
This is great for development, but not so good for production. We are exposing vulnerabilities with the codebase which can pose as a major security risk, particularly if it ends up in the wrong hands.
Introducing custom middleware
To reduce the security risk, we can override the HTTP response with a more friendly message. This can be done by writing our own custom middleware, or using the UseExceptionHandler
extension method in an ASP.NET Core Program.cs
file that appears in the WebApplication
type.
// Program.cs
var builder = WebApplication.CreateBuilder(args);
...
var app = builder.Build();
app.UseExceptionHandler(builder =>
{
builder.Run(async context =>
{
// Overrides the HTTP response
context.Response.StatusCode = 500;
context.Response.ContentType = Text.Html;
await context.Response.WriteAsync("<p>An error has occured</p>");
});
});
...
app.Run();
In this instance, we are setting the status code as 500, the content type as text/html
and the response body as <p>An error has occured</p>
.
When we run the endpoint now and the exception occurs, rather than having all the debug information, we now have our friendly message returned as part of the response body.
This way will continue to work fine in .NET 8 projects. However, there is a better way of handling exceptions.
Introducing the exception handling middleware
.NET 8 has launched the exception handling middleware meaning we can create classes and register them to the application.
For this to happen, we have to implement the IExceptionHandler
interface and the TryHandleAsync
method in a new class.
// MyExceptionHandler.cs
public class MyExceptionHandler : IExceptionHandler
{
public async ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
httpContext.Response.StatusCode = 500;
httpContext.Response.ContentType = Text.Html;
await httpContext.Response.WriteAsync("<p>An error has occured</p>");
return true;
}
}
With the exception handler created, we need to register it in Program.cs
by calling the AddExceptionHandler
extension method that appears in the IServiceCollection
instance, adding the class name as the generic type.
C# coding challenges
As a result, we can remove the code that appears inside the UseExceptionHandler
extension method in the WebApplication
instance.
// Program.cs
var builder = WebApplication.CreateBuilder(args);
...
// Register the exception handler middleware to the application.
builder.Services.AddExceptionHandler<MyExceptionHandler>();
var app = builder.Build();
app.UseExceptionHandler(builder =>
{
});
...
app.Run();
Just a note that the development exception page will take precedence over any exception handler middleware. As a result, if app.UseDeveloperExceptionPage();
is used in Program.cs
, it will need to be removed otherwise it will not work.
When running the application, the friendly error still gets shown as part of the HTTP response body but it is now going through the MyExceptionHandler
class.
More than one exception handling middleware
When writing a exception handler middleware class, the TryHandleAsync
method returns a boolean. If this is set to true
, no other exception handling middleware that is registered to the application will run.
However, if it is set to false
, it will try the next exception handling middleware.
The order is determined by which one is registered first in the Program.cs
file.
In this instance, we have MyExceptionHandler
class registered before MyException2Handler
. If the TryHandleAsync
method returns false
in MyExceptionHandler
, it will run the MyException2Handler
to handle the error.
// Program.cs
var builder = WebApplication.CreateBuilder(args);
...
// If there is an exception, this will run first
builder.Services.AddExceptionHandler<MyExceptionHandler>();
// This will only run if the previous handler is not able to handle the exception
builder.Services.AddExceptionHandler<MyException2Handler>();
var app = builder.Build();
app.UseExceptionHandler(builder =>
{
});
...
app.Run();
Watch our video
Watch our video where we try out the new exception handling middleware and show what happens when we have more than one exception handler registered to an ASP.NET Core Web API.