REST
REST — архитектурный стиль для распределённых систем. В HTTP API его основные идеи: ресурс имеет URI, клиент передаёт представление ресурса, запрос самодостаточен, а стандартная семантика методов и кодов ответа остаётся видимой.
Ресурс и представление
GET /orders/42 HTTP/1.1
Host: api.example.test
Accept: application/jsonHTTP/1.1 200 OK
Content-Type: application/json
ETag: "v7"
{"id":42,"status":"PAID"}/orders/42 обозначает ресурс, JSON — одно из его представлений. URI лучше строить из существительных; действие выражает HTTP method или отдельный доменный ресурс:
POST /orders/42/cancellationsЭто часто яснее, чем POST /orders/42/cancel, если отмена имеет собственное состояние и аудит.
Семантика методов
| Метод | Назначение | Safe | Idempotent |
|---|---|---|---|
| GET | получить представление | да | да |
| HEAD | получить headers без body | да | да |
| POST | создать подчинённый ресурс или запустить обработку | нет | нет по умолчанию |
| PUT | полностью заменить состояние по известному URI | нет | да |
| PATCH | частично изменить ресурс | нет | не гарантируется |
| DELETE | удалить ресурс | нет | да по семантике результата |
Идемпотентность означает одинаковое целевое состояние после повторов, а не одинаковый status code или отсутствие журналирования.
Коды ответа
200 OK— операция успешна, есть представление результата.201 Created— ресурс создан; полезенLocation.202 Accepted— запрос принят, обработка ещё не завершена.204 No Content— успех без body.400 Bad Request— синтаксис или общая валидация запроса.401 Unauthorized— аутентификация отсутствует/неуспешна.403 Forbidden— личность известна, доступа нет.404 Not Found— ресурс не найден или скрыт политикой доступа.409 Conflict— конфликт текущего состояния, например версия устарела.412 Precondition Failed— не выполненоIf-Match.422 Unprocessable Content— формат понятен, доменные поля недопустимы.429 Too Many Requests— ограничение частоты.500/502/503/504— ошибки сервера или downstream.
Конкурентное изменение
PUT /orders/42 HTTP/1.1
If-Match: "v7"
Content-Type: application/json
{"id":42,"status":"CANCELLED"}Сервер применяет изменение, только если ETag всё ещё "v7". Иначе отвечает 412, не затирая чужое обновление.
Stateless не означает “без состояния”
Сервер может хранить данные, кэш и сессию. Ограничение stateless означает, что каждый запрос содержит достаточно контекста для обработки и серверу не требуется скрытое состояние диалога на конкретном экземпляре. Это помогает маршрутизировать запрос на любой instance.
Pagination и сетевой N+1
GET /orders?limit=50&cursor=next-tokenЕсли клиент получает список, а затем делает отдельный запрос для каждой строки, локальный N+1 превращается в сетевой. В рабочем случае я заранее загрузил доступные набором данные и дедуплицировал оставшиеся одиночные обращения. См. Кейс сетевой N plus 1.
Типичные сбои
- Возвращать
200с текстом ошибки и ломать стандартную обработку клиента. - Использовать GET для изменения состояния: кэш, crawler или retry могут повторить его.
- Считать любой PATCH идемпотентным.
- Возвращать entity ORM напрямую и случайно раскрывать поля или запускать lazy loading.
- Делать бесконечную offset-pagination на изменяемом наборе без стабильной сортировки.
- Менять контракт ответа без обратной совместимости.
Вопросы для собеседования
Чем resource отличается от representation? Resource — логическая сущность по URI, representation — её передаваемая форма.
Почему DELETE идемпотентен, даже если второй ответ 404? Повтор не меняет целевое состояние: ресурс остаётся удалённым.
Когда вернуть 202? Когда обработка асинхронна; клиенту нужен способ узнать её статус.
Как защититься от lost update? Версионирование, ETag и условные запросы If-Match.
REST требует JSON? Нет. Формат representation выбирается отдельно через media type.
См. Веб-сервисы, Веб протоколы, Отличия POST и PUT и PATCH методов HTTP.