Ответы и ошибки: IResult, ProblemDetails, статус-коды

↑ 3.3 Web API: контроллеры, Minimal API, валидация · ← Предыдущая · → Следующая

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

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

Знание статус-кодов и единого формата ошибок (RFC 9457) — признак зрелого API-дизайна.

Объяснение

КодКогда
200 OKуспешное чтение/операция с телом
201 Createdсоздан ресурс + заголовок Location
202 Acceptedпринято, обработка асинхронна
204 No Contentуспех без тела (обновление, удаление)
400 Bad Requestневерный формат запроса
401 Unauthorizedне аутентифицирован
403 Forbiddenнет прав
404 Not Foundресурс не найден
409 Conflictконфликт состояния/версии
422 Unprocessable Entityсинтаксис верен, бизнес-правило нарушено
429 Too Many Requestsлимит запросов
500 / 503ошибка сервера / временная недоступность
app.MapPost("/orders", async (CreateOrderRequest r, IOrderService s) =>
{
    var id = await s.CreateAsync(r);
    return Results.Created($"/orders/{id}", new { id });
});
 
app.MapGet("/orders/{id:guid}", async (Guid id, IOrderService s) =>
    await s.FindAsync(id) is { } o ? Results.Ok(o) : Results.Problem(statusCode: 404, title: "Order not found"));
 
// TypedResults для OpenAPI-типизации
static async Task<Results<Ok<OrderDto>, NotFound>> Get(Guid id, IOrderService s) => ...

ProblemDetails (RFC 9457)

Единый формат ошибки: type, title, status, detail, instance и расширения (errors, traceId).

builder.Services.AddProblemDetails(o => o.CustomizeProblemDetails = c =>
    c.ProblemDetails.Extensions["traceId"] = c.HttpContext.TraceIdentifier);

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

  • 200 с текстом «ошибка» в теле — антипаттерн.
  • Не отдавайте стек и внутренние детали в production.
  • 401 vs 403: без токена/невалидный — 401; токен валиден, но прав нет — 403.
  • 404 вместо 403 иногда используют, чтобы не раскрывать существование ресурса.
  • Идемпотентность: PUT/DELETE идемпотентны, POST — нет (нужен Idempotency-Key).

Практика

  1. Включите ProblemDetails и убедитесь, что все ошибки приходят в едином формате.
  2. Верните TypedResults и посмотрите схему в Swagger.
  3. Реализуйте 409 при конфликте версии (ETag/rowversion).

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

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