Skip to content Skip to footer

Exceptional Control: .NET 8.0 Global Handling Insights

In .NET 8, the IExceptionHandler interface allows for global exception handling. This interface helps manage unhandled exceptions in your application, enabling you to handle errors more gracefully and improve the user experience.

To start with IExceptionHandler, you need to implement the interface in your application code. This process entails creating a class that implements the IExceptionHandler interface and the TryHandleAsync method.

Let’s create the class and implement the method.

Step 1: Create Custom Exception Handler

using System.Net;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;

namespace Example.Api.Extensions;

public class CustomExceptionHandler : IExceptionHandler
{
  
}

Step 2: Add Logging, Constructor & TryHandleAsync

public class CustomExceptionHandler : IExceptionHandler
{
    private readonly ILogger<CustomExceptionHandler> _logger;
    /// <summary>
     /// Constructor
     /// </summary>
     /// <param name="logger"></param>
     public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
     {
         _logger = logger;
     }
    /// <summary>
    /// Log the Exception in Log File
    /// </summary>
    /// <param name="httpContext"></param>
    /// <param name="exception"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async ValueTask<bool> TryHandleAsync(
        HttpContext httpContext,
        Exception exception,
        CancellationToken cancellationToken)
    {
       
    }
}

Step 3:  IExceptionHandler Implementation

public class CustomExceptionHandler : IExceptionHandler
{
    private readonly ILogger<CustomExceptionHandler> _logger;
    /// <summary>
     /// Constructor
     /// </summary>
     /// <param name="logger"></param>
     public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
     {
         _logger = logger;
     }
    /// <summary>
    /// Log the Exception in Log File
    /// </summary>
    /// <param name="httpContext"></param>
    /// <param name="exception"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async ValueTask<bool> TryHandleAsync(
        HttpContext httpContext,
        Exception exception,
        CancellationToken cancellationToken)
    {
        var problemDetails = new ProblemDetails
        {
            Status = (int)HttpStatusCode.InternalServerError,
            Type = exception.GetType().Name,
            Title = "An unexpected error occurred",
            Detail = exception.Message,
            Instance = $"Method => {httpContext.Request.Method}, Path => {httpContext.Request.Path}"
        };
        _logger.LogError(exception, "Unhandled exception occured. Please check");
        await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken: cancellationToken);
        return true;
    }
}

If you’ve noticed, we’ve used the ProblemDetails class. Let’s explore why this class is necessary.

The status codes from the RFC7230 specification often fail to provide enough error details. Based on the response, we can understand the error and take appropriate action.

The following excerpt from RFC7230 illustrates a typical scenario:

“For example, consider a response that indicates that the client’s account doesn’t have enough credit. The 403 Forbidden status code might be considered most appropriate, as it informs HTTP-generic software (like client libraries, caches, and proxies) about the general semantics of the response.

However, that does not provide the API client with enough information about why the request was forbidden, the current account balance, or how to correct the problem. If these details are included in the response body in a machine-readable format, the client can respond appropriately; for instance, by initiating a transfer of more credit into the account.

The RFC7807 specification helps to address this issue. The RFC7159 specification describes the standard model for the problem details (JSON) object.

The sample HTTP response as below

HTTP/1.1 500 Internal Server Error

Content-Type: application/problem+json

Content-Language: en

{
  type: IndexOutOfRangeException,
  title: An unexpected error occured,
  status: 500,
  instance: Method => GET, Path => /weatherforecast,
  detail: Index was outside the bounds of the array
}

Step 4: Adding the custom exception in our application

We need to add the following two highlighted lines to our Program.cs.

using Example.Api.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddExceptionHandler<CustomExceptionHandler>();
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler(ueh => { });

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseStatusCodePages();
app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
    {
        var x = summaries[12];
        
        var forecast = Enumerable.Range(1, 5).Select(index =>
                new WeatherForecast
                (
                    DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                    Random.Shared.Next(-20, 55),
                    summaries[Random.Shared.Next(summaries.Length)]
                ))
            .ToArray();
        return forecast;
    })
    .WithName("GetWeatherForecast")
    .WithOpenApi();

app.Run();

CONCLUSION

In .NET 8, the IExceptionHandler interface allows for global exception handling, improving error management and user experience. The interface is implemented by creating a class that includes the TryHandleAsync method. The ProblemDetails class is used to provide detailed error information, addressing the limitations of the RFC7230 specification. The custom exception handler is then added to the application in the Program.cs file.