Course Content
ASP.NET Core Web API Fundamentials
0/2
Method Safety And Method Idempotency
0/1
Working With OPTIONS and HEAD Requests
0/2
Root Document in ASP.NET Core Web API
0/1
Ultimate ASP.NET Core Web API

After the release of .NET 8, we can use this new interface to handle exceptions globally in our project. The IExceptionHandler interface has a single method member named TryHandleAsync:

ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken);

With this method, we try to handle the specified exception asynchronously within the ASP.NET Core pipeline. Basically, we can use our existing error-handling logic inside this method and everything should work as before. One great advantage of this implementation is that we register the class, where we handle our exceptions, as a service, which means we can use other services inside that class. If you remember our previous implementation, we had to retrieve the ILoggerManager service first and then send it as an argument to the extension method.

Now, we don’t have to do that, we can simply inject the ILoggerManager service and use it while handling exceptions.

To start, let’s create a new GlobalExceptionHandler class inside the main project:

using Microsoft.AspNetCore.Diagnostics;

namespace CompanyEmployees;

public class GlobalExceptionHandler : IExceptionHandler
{
    public ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, 
        CancellationToken cancellationToken)
    {

    }
}

You can see that our class inherits from the IExceptionHandler interface and implement the TryHandleAsync method. Now, let’s fully implement the method and modify the class:

public class GlobalExceptionHandler : IExceptionHandler
{
    private readonly ILoggerManager _logger;

    public GlobalExceptionHandler(ILoggerManager logger)
    {
        _logger = logger;
    }

    public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, 
        CancellationToken cancellationToken)
    {
        httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        httpContext.Response.ContentType = "application/json";

        _logger.LogError($"Something went wrong: {exception.Message}");

        await httpContext.Response.WriteAsJsonAsync(new ProblemDetails
        {
            Title = "An error occurred",
            Status = httpContext.Response.StatusCode,
            Detail = "Internal Server Error.",
            Type = exception.GetType().Name
        });

        return true;
    }
}

This time, we inject the ILoggerManager service and use it inside the TryHandleAsync method to log our message. The rest of the code is familiar with a difference that we have to return true from this method to state that the execution in the pipeline is over. With this in place, we can register this class as a service, and register a handler middleware in the Program class:

builder.Services.AddAutoMapper(typeof(Program));
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();

builder.Services.AddControllers()
    .AddApplicationPart(typeof(CompanyEmployees.Infrastructure.Presentation.AssemblyReference).Assembly);

builder.Host.UseSerilog((hostContext, configuration) =>
{
    configuration.ReadFrom.Configuration(hostContext.Configuration);
});

var app = builder.Build();

//var logger = app.Services.GetRequiredService<ILoggerManager>();
//app.ConfigureExceptionHandler(logger);
app.UseExceptionHandler(opt => { });

Here, we use the AddExceptionHanlder method to register our GlobalExceptionHandler class as a service. Also, we hide our old code and call the UseExceptionHanlder method to add the middleware to the pipeline. That’s it. You can test this out with the same Postman request, and everything should work the same.

0% Complete