HTTP-коды и результаты действий
IActionResult и коды ответов: как вернуть 200, 201, 404 и почему это важно.
Суть: HTTP-ответ — это не только тело (JSON), но и статус-код, который говорит клиенту, что произошло: успех (2xx), ошибка клиента (4xx), ошибка сервера (5xx). В ASP.NET Core за это отвечают типы результатов вроде
Ok(),NotFound(),BadRequest().
Начинающие часто возвращают данные и забывают про код ответа. Но для API код — это контракт: фронтенд по нему понимает, показать ли данные, форму ошибки или редирект. Возвращать 200 при «не найдено» — типичная ошибка, ломающая клиентов.
Основные результаты
[HttpGet("{id:int}")]
public IActionResult Get(int id)
{
var user = _repo.Find(id);
if (user is null)
return NotFound(); // 404
return Ok(user); // 200 + JSON
}
[HttpPost]
public IActionResult Create(UserDto dto)
{
if (!ModelState.IsValid)
return BadRequest(ModelState); // 400
var created = _repo.Add(dto);
return CreatedAtAction(nameof(Get), new { id = created.Id }, created); // 201
}
Карта кодов
| Код | Метод | Когда |
|---|---|---|
| 200 OK | Ok() | Успешное чтение/обновление |
| 201 Created | CreatedAtAction() | Создан новый ресурс |
| 204 No Content | NoContent() | Успех без тела (удаление) |
| 400 Bad Request | BadRequest() | Невалидные данные клиента |
| 404 Not Found | NotFound() | Ресурс не существует |
Как работает под капотом
Когда вы возвращаете Ok(user), создаётся объект OkObjectResult со статусом 200 и телом user. Фреймворк выполняет этот результат: выставляет статус-код, сериализует тело в JSON, пишет заголовки. IActionResult — это абстракция «отложенного ответа»: метод описывает что вернуть, а фреймворк решает как. Типизированные результаты (ActionResult<User>) добавляют ещё и информацию о типе для документации Swagger.
Частые ошибки
- Возвращать 200 при ошибке. Клиент не отличит успех от провала — всегда возвращайте корректный код.
- Кидать исключение вместо 404. «Не найдено» — это нормальная ситуация, а не ошибка сервера (500).
- Отдавать стектрейс в ответе. Внутренние ошибки идут в 500 без деталей наружу (детали — в логи).
Best practices
- Используйте
CreatedAtAction/CreatedAtRouteдля 201 — они ещё и ставят заголовокLocationна новый ресурс. - Для единообразных ошибок применяйте
ProblemDetails(стандарт RFC 7807) —[ApiController]делает это автоматически. - Возвращайте
ActionResult<T>вместоIActionResult, когда тип данных известен — лучше для документации и типобезопасности.
Семейства статус-кодов и что они сообщают клиенту
Коды ответов делятся на классы, и каждый класс несёт смысл. 2xx — успех (200 OK, 201 Created, 204 No Content). 3xx — перенаправление (301/302). 4xx — ошибка на стороне клиента: 400 (плохой запрос), 401 (не аутентифицирован), 403 (нет прав), 404 (не найдено), 409 (конфликт, например нарушение уникальности), 422 (данные не прошли семантическую проверку). 5xx — ошибка сервера (500, 503). Клиент по первой цифре уже понимает стратегию: ретраить, показать форму ошибки, отправить на логин.
Особенно важно различать 400, 401, 403 и 404, потому что их часто путают. 400 — «ты прислал ерунду». 401 — «представься». 403 — «ты известен, но сюда нельзя». 404 — «такого ресурса нет». Каждый из них ведёт клиента к разному действию, и подмена одного другим ломает поведение фронтенда и сторонних потребителей API.
ProblemDetails — стандартный формат ошибок
Чтобы ошибки были единообразными, существует стандарт RFC 7807 ProblemDetails: ответ об ошибке — это JSON с полями type, title, status, detail, instance. ASP.NET Core с атрибутом [ApiController] автоматически возвращает ошибки валидации и многие 4xx/5xx именно в этом формате. Клиентам удобно: они обрабатывают один предсказуемый формат, а не зоопарк самописных структур ошибок от каждого эндпоинта.
Возврат ActionResult<T> вместо IActionResult даёт ещё одно преимущество: компилятор и генератор документации знают тип успешного ответа. Вы можете вернуть и Ok(dto), и NotFound() из одного метода, при этом Swagger покажет схему T для 200. Это сочетание типобезопасности и самодокументируемости — признак зрелого API.
Итог: статус-код — часть контракта API. Возвращайте корректные коды через хелперы, не маскируйте ошибки под 200. Дальше переходим к проектированию полноценного REST Web API.