← Назад к вопросам

Как обрабатывать глобальные исключения в ASP.NET Core Web API? Что такое Exception Middleware?

1.8 Middle🔥 241 комментариев
#ASP.NET и Web API#Архитектура и микросервисы

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Глобальная обработка исключений в ASP.NET Core Web API

В ASP.NET Core обработка глобальных исключений — это фундаментальный механизм для централизованного управления ошибками, обеспечивающий надежность, консистентность ответов и безопасность API. Ключевым инструментом для этого является Middleware, а специальный Exception Middleware позволяет перехватывать исключения на уровне всего pipeline обработки запроса.

Что такое Exception Middleware?

Middleware в ASP.NET Core — это компоненты, которые образуют конвейер обработки HTTP-запросов и ответов. Они выполняются последовательно и могут влиять на поток выполнения. Exception Middleware — это специализированный middleware, предназначенный для перехвата исключений, возникающих в любом месте этого конвейера или в приложении (например, в контроллерах, сервисах). Его основная задача — преобразовать необработанные исключения в структурированные HTTP-ответы (обычно в формате JSON), чтобы клиенты получали понятные ошибки вместо "сырых" stack trace или пустых 500-х статусов.

Основные подходы к обработке глобальных исключений

1. Использование готового middleware UseExceptionHandler

ASP.NET Core предоставляет built-in middleware через метод UseExceptionHandler() в классе WebApplication. Это самый простой и рекомендуемый способ для большинства случаев.

// Program.cs или Startup.cs
app.UseExceptionHandler(appError =>
{
    appError.Run(async context =>
    {
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        context.Response.ContentType = "application/json";

        var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
        if (contextFeature != null)
        {
            var exception = contextFeature.Error;
            // Логирование исключения (например, через ILogger)
            logger.LogError(exception, "Global exception caught");

            // Создание структурированного ответа для клиента
            var errorResponse = new
            {
                StatusCode = context.Response.StatusCode,
                Message = "Internal Server Error",
                Detail = exception.Message,
                // Не включать stack trace в production для безопасности
                StackTrace = app.Environment.IsDevelopment() ? exception.StackTrace : null
            };

            await context.Response.WriteAsync(JsonSerializer.Serialize(errorResponse));
        }
    });
});

2. Создание кастомного Exception Middleware

Для более сложных сценариев (например, классификация исключений, разные форматы ответов) можно создать собственный middleware.

// CustomExceptionMiddleware.cs
public class CustomExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<CustomExceptionMiddleware> _logger;

    public CustomExceptionMiddleware(RequestDelegate next, ILogger<CustomExceptionMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (ValidationException ex)
        {
            // Обработка специфичных исключений (например, валидации)
            _logger.LogWarning(ex, "Validation error");
            context.Response.StatusCode = 400;
            await context.Response.WriteAsJsonAsync(new { Error = ex.Message });
        }
        catch (NotFoundException ex)
        {
            _logger.LogWarning(ex, "Resource not found");
            context.Response.StatusCode = 404;
            await context.Response.WriteAsJsonAsync(new { Error = "Resource not found" });
        }
        catch (Exception ex)
        {
            // Глобальная обработка всех остальных исключений
            _logger.LogError(ex, "Unhandled exception");
            context.Response.StatusCode = 500;
            await context.Response.WriteAsJsonAsync(new { Error = "Internal server error" });
        }
    }
}

// Затем регистрируем в Program.cs
app.UseMiddleware<CustomExceptionMiddleware>();

3. Фильтры исключений (Exception Filters)

Для обработки исключений на уровне контроллеров или действий можно использовать Exception Filters. Они полезны, когда нужна более локализованная обработка, но также могут работать глобально.

// Глобальный фильтр в Program.cs
builder.Services.AddControllers(options =>
{
    options.Filters.Add<GlobalExceptionFilter>();
});

// GlobalExceptionFilter.cs
public class GlobalExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        if (context.Exception is ValidationException)
        {
            context.Result = new BadRequestObjectResult(context.Exception.Message);
            context.ExceptionHandled = true;
        }
        // ... обработка других типов
    }
}

Ключевые принципы и best practices

  • Логирование: Все исключения должны логироваться (например, через ILogger) для диагностики. Используйте разные уровни (Error, Warning) в зависимости от типа.
  • Консистентность формата ответа: Все ошибки должны возвращаться в одинаковом JSON-формате (например, { error: { code, message, details } }).
  • Безопасность: В production избегайте возврата stack trace, внутренних деталей реализации или чувствительной информации в ответах клиентам.
  • Классификация исключений: Маппинг специфичных типов исключений на соответствующие HTTP статусы (400 для валидации, 404 для отсутствующих ресурсов, 500 для внутренних ошибок).
  • Ordering middleware: Exception middleware должен регистрироваться раньше других middleware (например, аутентификации, авторизации), но после middleware логирования. Это гарантирует, что он перехватит исключения из всего pipeline.
// Правильный порядок в Program.cs
app.UseHttpsRedirection();
app.UseCustomExceptionMiddleware(); // Exception middleware здесь
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
  • Использование ProblemDetails: ASP.NET Core предлагает стандартный формат ProblemDetails (RFC 7807) для ошибок. Его можно автоматически генерировать.
app.UseExceptionHandler(exceptionHandlerApp =>
{
    exceptionHandlerApp.Run(async context =>
    {
        var problemDetailsService = context.RequestServices.GetRequiredService<IProblemDetailsService>();
        await problemDetailsService.WriteAsync(new ProblemDetailsContext { HttpContext = context });
    });
});

Итог

Exception Middleware в ASP.NET Core — это централизованный, гибкий и мощный механизм для обработки исключений. Он позволяет отделить логику обработки ошибок от бизнес-кода, обеспечивает единый формат ответов и повышает надежность API. Выбор между UseExceptionHandler, кастомным middleware или фильтрами зависит от сложности требований, но в большинстве случаев готовый middleware с кастомной логикой является оптимальным решением для глобальной обработки исключений в Web API.

Как обрабатывать глобальные исключения в ASP.NET Core Web API? Что такое Exception Middleware? | PrepBro