Глобальная обработка ошибок: IExceptionHandler, middleware

↑ 3.5 Инфраструктура приложения: ошибки, health checks, фоновые задачи · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

«Как в вашем API обрабатываются исключения?» — ждут единое место, ProblemDetails и отсутствие утечки деталей.

Объяснение

Исключения не должны обрабатываться в каждом action try/catch. Один обработчик на входе конвейера преобразует их в ответ.

public class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> log) : IExceptionHandler
{
    public async ValueTask<bool> TryHandleAsync(HttpContext ctx, Exception ex, CancellationToken ct)
    {
        var (status, title) = ex switch
        {
            NotFoundException   => (404, "Not found"),
            ConflictException   => (409, "Conflict"),
            ValidationException => (422, "Validation failed"),
            _                   => (500, "Unexpected error"),
        };
        if (status == 500) log.LogError(ex, "Unhandled exception {TraceId}", ctx.TraceIdentifier);
 
        ctx.Response.StatusCode = status;
        await ctx.Response.WriteAsJsonAsync(new ProblemDetails { Status = status, Title = title, Extensions = { ["traceId"] = ctx.TraceIdentifier } }, ct);
        return true;   // обработано
    }
}
 
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.AddProblemDetails();
app.UseExceptionHandler();
СпособКомментарий
IExceptionHandler (.NET 8+)рекомендуется, цепочка обработчиков
UseExceptionHandler + lambdaпростой вариант
Exception filter MVCтолько MVC, не ловит middleware
Result patternожидаемые ошибки без исключений

Нюансы и подводные камни

  • Исключения для ожидаемых бизнес-ситуаций дороги и «шумят»: рассмотрите Result<T>.
  • Не возвращайте ex.Message и стек клиенту в проде.
  • UseExceptionHandler ставьте первым.
  • Логируйте один раз: обработчик логирует, остальные слои не дублируют.
  • OperationCanceledException из-за отмены клиента — не ошибка (499/без ответа).

Практика

  1. Добавьте IExceptionHandler и маппинг доменных исключений в коды.
  2. Добавьте traceId в ответ и найдите его в логах.
  3. Замените исключения на Result<T> в одном сценарии.

Вопросы с ответами

Связанные темы