Шаг 18: Обработка ошибок
Middleware оборачивает весь pipeline в try-catch и преобразует исключения в структурированные HTTP-ответы.
ExceptionHandlingMiddleware.cs
В папке Middleware проекта API:
using FluentValidation;
using OrderManagement.Domain.Exceptions;
namespace OrderManagement.API.Middleware;
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
public ExceptionHandlingMiddleware(
RequestDelegate next,
ILogger<ExceptionHandlingMiddleware> logger)
{ _next = next; _logger = logger; }
public async Task InvokeAsync(HttpContext context)
{
try { await _next(context); }
catch (Exception ex)
{
_logger.LogError(ex, "Error: {Message}", ex.Message);
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(
HttpContext context, Exception exception)
{
var (statusCode, title) = exception switch
{
OrderNotFoundException => (404, "Not found"),
DomainException => (400, "Business rule violation"),
ValidationException => (400, "Validation error"),
_ => (500, "Internal server error")
};
var response = new
{
status = statusCode,
title,
detail = exception.Message,
errors = exception is ValidationException vex
? vex.Errors.Select(e => new
{ property = e.PropertyName,
message = e.ErrorMessage })
: null
};
context.Response.StatusCode = statusCode;
context.Response.ContentType = "application/json";
await context.Response.WriteAsJsonAsync(response);
}
}
Подключение в Program.cs
Добавьте строку перед app.MapControllers():
using OrderManagement.API.Middleware;
app.UseMiddleware<ExceptionHandlingMiddleware>();
app.MapControllers();
DomainException -> HTTP 400, OrderNotFoundException -> HTTP 404, ValidationException -> HTTP 400 со списком ошибок, всё остальное -> HTTP 500.