REST, коды ответов и формат ошибок
Аналитик всё чаще проектирует контракты сам, поэтому REST спрашивают почти на каждом собеседовании — и почти всегда с подвохом про коды ответов.
REST — архитектурный стиль, где система описывается через ресурсы (существительные), над которыми выполняются стандартные HTTP-методы, а состояние клиента не хранится на сервере между запросами.
Вопрос 1. Что такое REST и какие у него принципы?
Что проверяют. Не путаете ли вы «REST» и «любой HTTP API с JSON». Половина API в мире называется REST, будучи RPC поверх HTTP, — и это нормально, но аналитик обязан видеть разницу.
Ключевые принципы, которые стоит назвать:
- Ресурсная модель. URL адресует сущность, а не действие:
/orders/1024, а не/getOrderById?id=1024. Глагол несёт HTTP-метод. - Единообразный интерфейс. Один и тот же набор методов работает одинаково для всех ресурсов.
- Отсутствие состояния (stateless). Каждый запрос самодостаточен: сервер не помнит предыдущий. Отсюда — авторизация в каждом запросе и возможность горизонтального масштабирования.
- Кэшируемость. Ответ на GET может быть закэширован, и API должен это явно обозначать.
- Слои. Между клиентом и сервисом могут стоять шлюзы и балансировщики, клиент об этом не знает.
ПЛОХО (RPC в маске REST) ХОРОШО (ресурсный стиль)
POST /createOrder POST /orders
GET /getOrderList GET /orders?status=paid&page=2
POST /cancelOrderById POST /orders/1024/cancellation
GET /getUserOrders?u=7 GET /customers/7/orders
Отдельно оговорите действия, которые не ложатся на CRUD: отмена, подтверждение, пересчёт. Практика — оформлять их как подресурс-событие (POST /orders/1024/cancellation) либо как явный переход статуса. Это лучше, чем городить PATCH с волшебным полем action.
Вопрос 2. Какие коды ответов вы используете и когда?
Что проверяют. Знание не всей таблицы, а рабочего минимума и различий между парами, которые все путают: 401/403, 400/422, 404/410.
| Код | Когда |
| 200 OK | успешное чтение или изменение с телом ответа |
| 201 Created | создан новый ресурс; в заголовке Location — его адрес |
| 202 Accepted | запрос принят, обработка асинхронная; результат позже |
| 204 No Content | успех без тела (например, удаление) |
| 400 Bad Request | запрос синтаксически неверен: сломан JSON, не тот тип поля |
| 401 Unauthorized | не понятно, кто вы: токена нет или он невалиден |
| 403 Forbidden | понятно, кто вы, но вам нельзя |
| 404 Not Found | ресурса нет (или мы не хотим раскрывать, что он есть) |
| 409 Conflict | конфликт состояния: заказ уже отменён, версия устарела |
| 422 Unprocessable Entity | синтаксис верен, но нарушено бизнес-правило |
| 429 Too Many Requests | превышен лимит; ответ с Retry-After |
| 500 / 503 | наша ошибка / сервис временно недоступен |
Формулировка, которая звучит зрело: «4xx — клиент виноват, чинить на его стороне; 5xx — мы виноваты, клиенту имеет смысл повторить позже». Именно поэтому нельзя отдавать 200 с телом {"error": "..."}: клиентские библиотеки, ретраи и мониторинг ориентируются на код, и такой API невозможно нормально эксплуатировать.
Вопрос 3. Как вы описываете формат ошибки?
Что проверяют. Думаете ли вы о том, кто и как будет эти ошибки обрабатывать. Хорошая ошибка машиночитаема и одновременно пригодна для показа пользователю.
{
"type": "https://api.shop.ru/errors/validation",
"title": "Ошибка валидации",
"status": 422,
"code": "ORDER_EMPTY",
"detail": "Заказ должен содержать хотя бы одну позицию",
"traceId": "b7c1f0a2-3d44-4a11-9f0e-77d1a2c9e5b3",
"errors": [
{ "field": "items", "code": "MIN_ITEMS", "message": "минимум 1 позиция" },
{ "field": "items[0].qty", "code": "MIN_VALUE", "message": "должно быть больше 0" }
]
}
Что здесь важно и почему:
- Стабильный
code— по нему клиент ветвит логику. Текстdetailможно менять и переводить, код — нельзя, это часть контракта. - Список
errorsс указанием поля — чтобы фронтенд подсветил конкретные поля формы, а не показал одно общее сообщение. - Все ошибки сразу, а не первая попавшаяся — иначе пользователь исправляет форму по одной ошибке за раз.
- traceId — чтобы поддержка по обращению пользователя нашла запрос в логах.
- Никаких внутренних деталей: стектрейсов, SQL-запросов, имён таблиц. Это и утечка, и лишний шум.
Структура выше близка к стандарту RFC 9457 (Problem Details) — упомянуть его на собеседовании полезно, но важнее объяснить смысл полей.
Типичные ошибки кандидатов
- Называют глаголы в URL нормой: «главное, чтобы работало». Работать будет, но это не REST, и на вопрос надо отвечать честно: «это RPC-стиль, у него свои плюсы».
- Путают 401 и 403. Мнемоника: 401 — «я тебя не узнаю», 403 — «я тебя узнал, тебе нельзя».
- Отдают 200 на ошибку. Ломается всё: ретраи, алерты, метрики.
- Возвращают 404 на пустой список. Пустой список — это 200 и
[]; 404 означает, что нет самого ресурса-коллекции. - Не описывают пагинацию и сортировку у списочных методов, и её потом «додумывает» разработчик.
- Забывают, что stateless означает передачу авторизации в каждом запросе, и проектируют «сессию» на стороне API.
Как ответить кратко
REST — это ресурсный стиль: URL адресует сущность существительным, действие несёт HTTP-метод, каждый запрос самодостаточен, ответы кэшируемы. Действия вне CRUD оформляю подресурсом:
POST /orders/1024/cancellation. Коды: 201 при создании с заголовком Location, 202 для асинхронной обработки, 400 — сломан синтаксис, 422 — нарушено бизнес-правило, 401 — не узнали, 403 — узнали и запрещаем, 409 — конфликт состояния, 429 с Retry-After при лимитах. 4xx — вина клиента, 5xx — наша; отдавать 200 с ошибкой в теле нельзя, это ломает ретраи и мониторинг. Ошибку описываю машиночитаемо: стабильный код, список ошибок по полям и traceId для поддержки.