Как обрабатывать глобальные исключения в ASP.NET Core Web API? Что такое Exception Middleware?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Глобальная обработка исключений в 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.