Gestion centralisée des exceptions dans ASP.NET Core

Filtres d'exception asynchrones

ASP.NET Core propose plusieurs mécanismes pour intercepter et traiter les erreurs au niveau applicatif. Bien que l'interface IExceptionFilter soit disponible, l'approche asynchrone via IAsyncExceptionFilter offre une meilleure compatibilité avec les opérations non bloquantes.

public class GlobalErrorHandler : IAsyncExceptionFilter
{
    private readonly ILogger<GlobalErrorHandler> _log;

    public GlobalErrorHandler(ILogger<GlobalErrorHandler> logger) => _log = logger;

    public Task OnExceptionAsync(ExceptionContext exceptionContext)
    {
        _log.LogError(exceptionContext.Exception, "Échec du traitement");

        if (!exceptionContext.ExceptionHandled)
        {
            exceptionContext.Result = new ObjectResult(new { error = "Service temporairement indisponible" })
            {
                StatusCode = StatusCodes.Status500InternalServerError
            };
            exceptionContext.ExceptionHandled = true;
        }

        return Task.CompletedTask;
    }
}

Pour activer ce filtre, on l'enregistre dans la configuration des services MVC :

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add(typeof(GlobalErrorHandler));
});

Middleware personnalisé pour les erreurs

Une alternative consiste à créer un middleware qui encapsule l'ensemble du pipeline de requêtes. Cette méthode capture les exceptions survenant avant même l'entrée dans le système MVC.

public class ExceptionInterceptionMiddleware
{
    private readonly RequestDelegate _nextStep;
    private readonly ILogger<ExceptionInterceptionMiddleware> _logger;

    public ExceptionInterceptionMiddleware(RequestDelegate next, ILogger<ExceptionInterceptionMiddleware> logger)
    {
        _nextStep = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext httpContext)
    {
        try
        {
            await _nextStep(httpContext);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Exception non gérée");
            
            httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
            httpContext.Response.ContentType = "application/problem+json";
            
            await httpContext.Response.WriteAsJsonAsync(new 
            { 
                title = "Erreur interne",
                detail = ex.Message
            });
        }
    }
}

Enregistrement du middleware dans le pipeline de requêtes :

app.UseMiddleware<ExceptionInterceptionMiddleware>();

Configuration native d'ASP.NET Core

Le framework intègre des middlewares de gestion d'erreurs préconfigurés selon l'environnement.

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler(appBuilder =>
    {
        appBuilder.Run(async context =>
        {
            context.Response.StatusCode = 500;
            context.Response.ContentType = "text/plain";
            
            var exceptionData = context.Features.Get<IExceptionHandlerFeature>();
            if (exceptionData != null)
            {
                await context.Response.WriteAsync($"Erreur critique : {exceptionData.Error.Message}");
            }
        });
    });
}

Ordre d'exécution des composants

L'ordre d'enregistrement des middlewares détermine leur portée de gestion. Un middleware ajouté en premier dans le pipeline pourra intercepter les erreurs des étapes suivantes. Les filtres MVC, quant à eux, se limitent aux exceptions survenant pendant l'exécution des contrôleurs et des actions.

Choix entre middleware et filtre

Le middleware UseExceptionHandler capture les erreurs globales, y compris celles survenant avant l'initialisation du contexte MVC. Les IAsyncExceptionFilter restent utiles pour appliquer une logique spécifique à certaines parties de l'application, comme la validation des modèles dans des contrôleurs particuliers.

Étiquettes: ASP.NET Core exception handling IAsyncExceptionFilter middleware C#

Publié le 12 juillet à 01h37