Вы когда-нибудь получали ответ 200 OK от сервера, а внутри JSON было поле "error": "User not found"? Если да, то вы знаете, как раздражает такое поведение. Или, наоборот, видели 404 Not Found, хотя ресурс вроде бы существует? Понимание того, как правильно использовать HTTP-статусы в REST API is архитектурный стиль проектирования веб-сервисов, основанный на принципах гипермедиа и идемпотентности операций. Это не просто набор цифр; это язык общения между клиентом (приложением или браузером) и сервером. Когда этот язык ломается, страдают все: разработчики тратят часы на дебаг, а пользователи видят непонятные сообщения об ошибках.
В этой статье мы разберем, какие коды подходят для каких ситуаций, почему важно не бояться «неудачных» статусов и как избежать типичных ловушек при проектировании интерфейсов.
Базовые принципы: зачем нужны статус-коды
HTTP-статус-коды is трехзначные числовые значения, возвращаемые сервером для индикации результата обработки запроса. Они делятся на пять классов:- 1xx: Информационные (редко используются в REST).
- 2xx: Успех - операция выполнена корректно.
- 3xx: Перенаправление - клиент должен сделать что-то еще.
- 4xx: Ошибка клиента - проблема на стороне отправителя запроса.
- 5xx: Ошибка сервера - проблема на стороне приложения или инфраструктуры.
200 не означает, что бизнес-логика успешна во всех смыслах, но он гарантирует, что сам запрос был доставлен и обработан без технических сбоев.
Класс 2xx: Как сообщать об успехе
Здесь начинается самое интересное, потому что многие разработчики используют только200 OK для всего подряд. Давайте разберем ключевые варианты.
- 200 OK: Стандартный ответ для успешных GET, PUT, PATCH и POST запросов, когда результат возвращается сразу. Например, вы запросили список товаров - получили JSON со списком и статус 200.
- 201 Created: Обязателен для POST-запросов, которые создают новый ресурс. Важно: в заголовке
Locationдолжен быть URL нового ресурса. Если вы создали пользователя, верните 201 и укажите, где его можно найти. - 204 No Content: Идеален для DELETE-операций или PUT/PATCH, где нет данных для возврата. Клиент знает, что операция прошла успешно, но не получает тело ответа. Это экономит трафик и время парсинга.
200 OK после создания ресурса. Технически это допустимо, но 201 Created более семантически точно и помогает клиентам (например, фронтенду) понимать, что объект появился впервые.
Класс 4xx: Ошибки на стороне клиента
Эти коды часто вызывают споры, потому что границы между ними размыты. Вот как их лучше применять:- 400 Bad Request: Общий код для синтаксических ошибок, неверного формата данных или неполных параметров. Используйте, если конкретнее сказать нельзя. Но старайтесь избегать этого кода там, где есть более точный вариант.
- 401 Unauthorized: Пользователь не аутентифицирован. Нет токена, токен истек или не передан. Здесь важно вернуть заголовок
WWW-Authenticate, чтобы клиент понял, какой механизм аутентификации требуется. - 403 Forbidden: Пользователь аутентифицирован, но не имеет прав доступа к ресурсу. Разница с 401 критична: 401 значит «кто ты?», 403 значит «я знаю, кто ты, но вход запрещен».
- 404 Not Found: Ресурс не найден. Может означать, что ID не существует, или что ресурс скрыт из-за недостаточных прав (хотя для последнего лучше подходит 403). Также используется для неизвестных маршрутов.
- 409 Conflict: Конфликт состояния. Классический пример: попытка создать ресурс с уже существующим уникальным email или именем. Это не ошибка валидации, а логическое противоречие.
- 422 Unprocessable Entity: Запрос понятен синтаксически, но семантически неверен. Часто используется вместо 400 для ошибок бизнес-логики, когда данные валидны формально, но непригодны для обработки.
| Статус | Когда использовать | Типичная ошибка в использовании |
|---|---|---|
| 400 | Неверный формат JSON, отсутствующие поля | Использование для любых ошибок валидации |
| 401 | Нет токена, токен недействителен | Использование вместо 403 при отсутствии прав |
| 404 | ID не существует, маршрут не найден | Скрытие ресурсов с правами через 404 (лучше 403) |
| 409 | Дубликат уникального значения | Использование 400 для конфликтов базы данных |
Класс 5xx: Когда виноват сервер
Если клиент видит5xx, значит, проблема не в нем. Но важно различать типы сбоев:
- 500 Internal Server Error: Ловушка для всех остальных случаев. Используется, когда исключение не перехвачено или ошибка непредвиденная. В продакшене всегда сопровождайте подробным логами на сервере, но в ответ клиенту отправляйте минимальное сообщение, чтобы не раскрывать архитектуру.
- 502 Bad Gateway: Сервер выступает прокси и получил некорректный ответ от вышестоящего узла. Часто возникает при проблемах с балансировщиками нагрузки или микросервисами.
- 503 Service Unavailable: Сервер временно перегружен или находится на техническом обслуживании. Обязательно используйте заголовок
Retry-After, чтобы указать клиенту, сколько ждать перед повторной попыткой.
Типичные ловушки и как их избежать
Даже опытные разработчики попадают в одни и те же грабли. Вот три самых частых:- «Всегда 200» подход. Некоторые дизайнеры API утверждают, что статус-коды должны отражать только транспортный уровень, а бизнес-ошибки - в теле. Это спорная позиция. Для публичных API лучше следовать стандарту: 4xx для проблем клиента, 5xx для сервера. Это упрощает работу с библиотеками и инструментами тестирования.
- Путаница 401 и 403. Если пользователь забыл пароль, ему нужен 401, чтобы приложение могло показать форму входа. Если он залогинен, но пытается удалить чужой пост - 403. Смешивание этих кодов ломает логику фронтенда.
- Отсутствие тела ответа при 4xx/5xx. Даже если статус говорит об ошибке, клиенту нужно знать, почему. Всегда возвращайте JSON с полем
messageи, желательно,errors(массив конкретных проблем). Пример:{"status": 400, "message": "Invalid input", "errors": [{"field": "email", "reason": "Must be valid format"}]}.
Практические советы для документации
Когда вы пишете OpenAPI/Swagger спецификацию, не ограничивайтесь описанием 200 ответа. Документируйте каждый возможный 4xx и 5xx код. Укажите, какие поля приходят в теле ошибки. Это спасет ваших потребителей API от гаданий. Также подумайте об идемпотентности. Если клиент получил таймаут, он может отправить запрос повторно. Для PUT и DELETE операции должны быть идемпотентными, чтобы повторный вызов не создавал дубликаты или не ломал состояние. Статус-коды здесь работают в связке с этим принципом: если операция уже выполнена, второй раз она должна вернуть тот же результат (например, 200 или 204), а не ошибку конфликта.Часто задаваемые вопросы
Какой статус-код использовать, если ресурс существует, но скрыт из-за прав?
Рекомендуется использовать 403 Forbidden. Код 404 раскрывает существование ресурса, что может быть проблемой безопасности. Однако, если ресурс действительно удален, тогда 404 - правильный выбор.
Можно ли возвращать 200 OK при создании нового ресурса?
Технически да, но лучше использовать 201 Created. Он явно указывает, что объект создан, и позволяет добавить заголовок Location. Это делает API более предсказуемым для клиентов.
Что делать, если произошла ошибка валидации нескольких полей?
Верните 400 Bad Request или 422 Unprocessable Entity и включите в тело ответа массив ошибок. Каждое поле должно иметь имя и причину ошибки. Это позволит фронтенду подсветить все проблемы сразу, а не по одному.
Нужно ли обрабатывать статус 429 Too Many Requests?
Да, если у вас есть лимиты запросов. Верните 429 вместе с заголовком Retry-After. Это вежливый способ сообщить клиенту, что он слишком быстро шлет запросы, и дать ему время на паузу.
Как выбрать между 400 и 422?
Используйте 400 для синтаксических ошибок (битый JSON, неправильный тип данных). Используйте 422, когда данные синтаксически верны, но не проходят бизнес-правила (например, возраст меньше 18 лет). Граница иногда размыта, главное - последовательность в вашем API.