REST — Архитектурный стиль для распределённых систем
REST определяет, как должны взаимодействовать компоненты распределённой системы. Это не протокол и не фреймворк, а набор архитектурных ограничений, формализованных Роем Филдингом в 2000 году. Понимание этих принципов помогает проектировать API, которые масштабируются, кэшируются и легко эволюционируют.
Содержание
- Что такое REST
- История появления
- REST, RESTful и REST API — одно и то же?
- Ограничения и принципы REST
- Ресурсы — ключевая абстракция
- HTTP-методы и их семантика
- Коды ответов: HTTP или REST?
- HATEOAS
- Примеры запросов
- REST vs другие подходы
- Лучшие практики
- Антипаттерны
- Частые заблуждения
Что такое REST
REST (Representational State Transfer) — это архитектурный стиль, описывающий набор ограничений для проектирования распределённых систем. REST — это не протокол, не фреймворк и не спецификация. Это набор принципов, определяющих, как должны взаимодействовать компоненты системы.
REST определён Роем Филдингом в его докторской диссертации «Architectural Styles and the Design of Network-based Software Architectures» (2000).
REST описывает, как должна быть устроена архитектура, чтобы она была:
- Масштабируемой — способность обрабатывать растущее количество запросов
- Простой — понятный и предсказуемый интерфейс
- Модифицируемой — возможность изменять компоненты без влияния на всю систему
История появления
Термин REST появился в 2000 году в диссертации Роя Филдинга, одного из авторов спецификации HTTP/1.1. Филдинг описывал архитектурный стиль, который лежал в основе успешных распределённых систем того времени — прежде всего, Всемирной паутины (World Wide Web).
Филдинг заметил, что веб работает именно потому, что следует определённым архитектурным ограничениям:
- Клиенты и серверы разделены
- Каждый запрос содержит всю необходимую информацию
- Ответы могут кэшироваться
- Интерфейс единообразен
Эти наблюдения были формализованы в виде стиля REST.
REST, RESTful и REST API — одно и то же?
Нет, это разные понятия, хотя на практике их часто путают.
REST
REST — это архитектурный стиль. Абстрактный набор принципов и ограничений. Сам по себе REST не привязан к какому-либо протоколу, хотя на практике почти всегда реализуется поверх HTTP.
RESTful
RESTful — это прилагательное, описывающее систему или API, которое следует принципам REST. Буквально — «REST-подобный».
Чем больше ограничений REST соблюдается, тем более «RESTful» является система.
REST API
REST API — это конкретная реализация API, которая следует принципам REST. Это то, с чем взаимодействуют разработчики на практике.
Сравнение
| Термин | Что это | Пример использования |
|---|---|---|
| REST | Архитектурный стиль (теория) | «Мы проектируем систему в стиле REST» |
| RESTful | Характеристика системы | «Наш API достаточно RESTful» |
| REST API | Конкретная реализация | «Вызови REST API для получения данных» |
На практике
В индустрии термины используются практически как синонимы:
- «REST API» и «RESTful API» — чаще всего означают одно и то же
- Строго говоря, «RESTful» подразумевает полное соответствие всем ограничениям REST
- Большинство современных API называют «RESTful», но не все они строго соответствуют оригинальным принципам Филдинга
Важно: Многие API, которые называют «RESTful», не соблюдают ключевое ограничение REST — HATEOAS (см. ниже). Рой Филдинг считает, что API без HATEOAS не является REST.
Ограничения и принципы REST
REST определяется шестью ограничениями (constraints). Если система соблюдает все шесть — она считается RESTful.
1. Клиент-серверная архитектура
Разделение ответственности между клиентом и сервером:
- Клиент отвечает за пользовательский интерфейс и взаимодействие с пользователем
- Сервер отвечает за хранение данных, бизнес-логику и управление ресурсами
Это разделение позволяет каждой стороне развиваться независимо.
┌──────────┐ ┌──────────┐
│ Клиент │ ◄─────► │ Сервер │
│ (UI) │ HTTP │ (Данные) │
└──────────┘ └──────────┘
2. Отсутствие состояния (Stateless)
Каждый запрос от клиента к серверу должен содержать всю необходимую информацию для обработки запроса. Сервер не хранит состояние сессии между запросами.
Stateless:
GET /api/users/123
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Вся информация для аутентификации — в каждом запросе.
Stateful (НЕ REST):
GET /api/users/123
Cookie: session_id=abc123
Сервер должен помнить, что session_id=abc123 соответствует пользователю.
Stateless не означает, что клиент не может быть аутентифицирован. Это означает, что информация об аутентификации передаётся в каждом запросе, а не хранится на сервере.
3. Кэшируемость
Ответы должны явно помечаться как кэшируемые или некэшируемые. Это позволяет клиентам (или промежуточным серверам) кэшировать ответы и повторно использовать их для одинаковых запросов.
HTTP/1.1 200 OK
Cache-Control: max-age=3600
ETag: "abc123"
Кэширование критично для масштабируемости — оно снижает нагрузку на сервер и уменьшает задержку.
4. Единообразие интерфейса (Uniform Interface)
Это ключевое ограничение REST. Все взаимодействия между клиентами и серверами должны происходить единообразно.
Единообразие интерфейса включает четыре подпринципа:
a) Идентификация ресурсов Каждый ресурс имеет уникальный идентификатор (URI):
GET /api/users/123
GET /api/orders/456
b) Манипуляция ресурсами через представления Клиент работает с ресурсом через его представление (JSON, XML и т.д.):
{
"id": 123,
"name": "Иван Петров",
"email": "ivan@example.com"
}
c) Самодостаточные сообщения Каждое сообщение содержит всю информацию, необходимую для его обработки (см. Stateless).
d) HATEOAS Ответы содержат ссылки на связанные ресурсы и доступные действия (см. раздел HATEOAS).
5. Слоистая система (Layered System)
Архитектура может состоять из нескольких слоёв, каждый из которых выполняет свою функцию:
- Клиент не знает, соединяется ли он напрямую с сервером или через прокси
- Промежуточные серверы могут кэшировать, балансировать нагрузку, аутентифицировать
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Клиент │ ──► │ Прокси │ ──► │ CDN │ ──► │ Сервер │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
6. Код по требованию (Code on Demand) — опционально
Сервер может передавать клиенту исполняемый код (JavaScript, апплеты). Это единственное опциональное ограничение.
Пример: сервер возвращает JavaScript-код для динамического расширения функциональности клиента.
Ресурсы — ключевая абстракция
В REST всё является ресурсом. Ресурс — это любая сущность, которую можно идентифицировать и с которой можно взаимодействовать.
Характеристики ресурса
- Идентифицируется URI — уникальным адресом
- Имеет представление — формат данных (JSON, XML, HTML и т.д.)
- Может иметь связанные ресурсы — связи с другими ресурсами
- Поддерживает стандартные операции — CRUD через HTTP-методы
Пример
Ресурс «Пользователь»:
- URI:
/api/users/123 - Представление: JSON-объект
- Операции: GET, PUT, DELETE
GET /api/users — список пользователей
POST /api/users — создать пользователя
GET /api/users/123 — получить пользователя
PUT /api/users/123 — обновить пользователя
DELETE /api/users/123 — удалить пользователя
HTTP-методы и их семантика
REST использует HTTP-методы для операций над ресурсами. Каждый метод имеет определённую семантику.
Основные методы
| Метод | Описание | Идемпотентный | Безопасный | Тело ответа |
|---|---|---|---|---|
GET | Получить ресурс | Да | Да | Да |
POST | Создать ресурс | Нет | Нет | Да |
PUT | Заменить ресурс полностью | Да | Нет | Да |
PATCH | Частично обновить ресурс | Нет | Нет | Да |
DELETE | Удалить ресурс | Да | Нет | Зависит |
HEAD | Получить метаданные ресурса | Да | Да | Нет |
OPTIONS | Получить поддерживаемые методы | Да | Да | Да |
Идемпотентность
Идемпотентный метод — метод, который при многократном вызове с одинаковыми параметрами даёт одинаковый результат.
GET /api/users/123— идемпотентный (100 вызовов = одинаковый результат)DELETE /api/users/123— идемпотентный (удаление уже удалённого ресурса не меняет состояние)POST /api/users— не идемпотентный (каждый вызов создаёт нового пользователя)
Безопасность
Безопасный метод — метод, который не изменяет состояние ресурса. Только GET и HEAD являются безопасными.
Соответствие методов операциям CRUD
| HTTP-метод | CRUD | Описание |
|---|---|---|
POST | Create | Создание нового ресурса |
GET | Read | Чтение ресурса |
PUT | Update | Полное обновление ресурса |
PATCH | Update | Частичное обновление ресурса |
DELETE | Delete | Удаление ресурса |
Коды ответов: HTTP или REST?
Коды ответов — это часть HTTP, а не REST.
REST использует HTTP как транспортный протокол, поэтому:
- Коды ответов (200, 201, 404, 500 и т.д.) определены в спецификации HTTP (RFC 7231, RFC 6585)
- REST рекомендует использовать семантику HTTP правильно
- Но сами коды существуют независимо от REST
Почему это важно
REST не определяет своих кодов ответов. Вместо этого REST говорит: «Используй HTTP правильно». Это означает:
- Используй коды ответов HTTP по их назначению
- Не возвращай
200 OKдля ошибок - Не возвращай
404 Not Foundдля существующих ресурсов
Категории кодов ответов HTTP
| Категория | Описание | Примеры |
|---|---|---|
1xx | Информационные | 100 Continue |
2xx | Успешные | 200 OK, 201 Created, 204 No Content |
3xx | Перенаправления | 301 Moved Permanently, 304 Not Modified |
4xx | Ошибки клиента | 400 Bad Request, 401 Unauthorized, 404 Not Found |
5xx | Ошибки сервера | 500 Internal Server Error, 503 Service Unavailable |
Наиболее используемые коды в REST API
2xx — Успех
| Код | Описание | Когда использовать |
|---|---|---|
200 OK | Успешный запрос | GET, PUT, PATCH — стандартный ответ |
201 Created | Ресурс создан | POST — после успешного создания |
202 Accepted | Запрос принят | Длительные операции (асинхронные) |
204 No Content | Успешно, нет содержимого | DELETE — после успешного удаления |
4xx — Ошибки клиента
| Код | Описание | Когда использовать |
|---|---|---|
400 Bad Request | Некорректный запрос | Невалидные данные, ошибка валидации |
401 Unauthorized | Требуется аутентификация | Отсутствует или невалиден токен |
403 Forbidden | Доступ запрещён | Аутентифицирован, но нет прав |
404 Not Found | Ресурс не найден | Несуществующий URI |
405 Method Not Allowed | Метод не поддерживается | GET на ресурсе, который поддерживает только POST |
409 Conflict | Конфликт состояния | Попытка создать ресурс, который уже существует |
422 Unprocessable Entity | Невалидные данные | Данные прошли синтаксическую проверку, но семантически некорректны |
429 Too Many Requests | Превышен лимит запросов | Rate limiting |
5xx — Ошибки сервера
| Код | Описание | Когда использовать |
|---|---|---|
500 Internal Server Error | Внутренняя ошибка сервера | Необработанное исключение |
502 Bad Gateway | Ошибка шлюза | Прокси не получил ответ от вышестоящего сервера |
503 Service Unavailable | Сервис недоступен | Сервер на обслуживании или перегружен |
Пример: правильный ответ REST API
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/users/123
{
"id": 123,
"name": "Иван Петров",
"email": "ivan@example.com",
"createdAt": "2024-01-15T10:30:00Z"
}
Пример: ошибка валидации
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Данные не прошли валидацию",
"details": [
{
"field": "email",
"message": "Некорректный формат email"
}
]
}
}
HATEOAS
HATEOAS (Hypermedia As The Engine Of Application State) — это ограничение REST, которое означает, что ответы должны содержать ссылки на связанные ресурсы и доступные действия.
HATEOAS — это то, что отличает настоящий REST от простого HTTP API. Без HATEOAS клиент должен знать структуру API заранее, а в настоящем REST клиент discovers действия через гипермедиа.
Пример без HATEOAS
GET /api/accounts/123
{
"id": 123,
"balance": 1000
}
Клиент должен заранее знать, что для пополнения счёта нужно отправить POST /api/accounts/123/deposit.
Пример с HATEOAS
GET /api/accounts/123
{
"id": 123,
"balance": 1000,
"links": [
{
"rel": "self",
"href": "/api/accounts/123"
},
{
"rel": "deposit",
"href": "/api/accounts/123/deposit",
"method": "POST"
},
{
"rel": "withdraw",
"href": "/api/accounts/123/withdraw",
"method": "POST"
},
{
"rel": "transactions",
"href": "/api/accounts/123/transactions"
}
]
}
Клиент динамически обнаруживает доступные действия через ссылки в ответе.
Почему HATEOAS редко используется
Несмотря на то, что HATEOAS — ключевое ограничение REST, большинство API его не реализуют:
- Усложняет ответы
- Требует дополнительной логики на сервере
- Большинство клиентов и так знают структуру API
- Альтернативы (OpenAPI/Swagger) предоставляют документацию отдельно
Мнение Роя Филдинга: «REST API без HATEOAS — это не REST API. Это просто HTTP API.»
Примеры запросов
GET — Получить ресурс
GET /api/users/123 HTTP/1.1
Host: api.example.com
Accept: application/json
Ответ:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"name": "Иван Петров",
"email": "ivan@example.com"
}
POST — Создать ресурс
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"name": "Мария Сидорова",
"email": "maria@example.com"
}
Ответ:
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/users/124
{
"id": 124,
"name": "Мария Сидорова",
"email": "maria@example.com"
}
PUT — Полностью обновить ресурс
PUT /api/users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"name": "Иван Петров",
"email": "ivan.new@example.com",
"role": "admin"
}
Ответ:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"name": "Иван Петров",
"email": "ivan.new@example.com",
"role": "admin"
}
PATCH — Частично обновить ресурс
PATCH /api/users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"email": "ivan.updated@example.com"
}
Ответ:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"name": "Иван Петров",
"email": "ivan.updated@example.com",
"role": "admin"
}
DELETE — Удалить ресурс
DELETE /api/users/123 HTTP/1.1
Host: api.example.com
Ответ:
HTTP/1.1 204 No Content
REST vs другие подходы
REST vs SOAP
| Характеристика | REST | SOAP |
|---|---|---|
| Протокол | HTTP | SOAP поверх HTTP/SMTP/TCP |
| Формат данных | JSON, XML, и др. | Только XML |
| Сложность | Простой | Сложный |
| Производительность | Быстрее | Медленнее |
| Кэширование | Встроенное (HTTP) | Требует дополнительной реализации |
| Типизация | Не строгая | Строгая (WSDL) |
| Использование | Веб-API, мобильные приложения | Корпоративные системы, банкинг |
REST vs GraphQL
| Характеристика | REST | GraphQL |
|---|---|---|
| Запрос | Фиксированные эндпоинты | Гибкий язык запросов |
| Ответ | Фиксированная структура | Клиент определяет структуру |
| Over-fetching | Возможна | Исключена |
| Under-fetching | Возможна | Исключена |
| Кэширование | Простое (HTTP) | Требует дополнительной реализации |
| Сложность | Простой | Сложнее |
| Версионирование | Часто требуется | Не требуется |
| Использование | Простые API, CRUD | Сложные данные, гибкие запросы |
REST vs gRPC
| Характеристика | REST | gRPC |
|---|---|---|
| Протокол | HTTP/1.1 или HTTP/2 | HTTP/2 |
| Формат данных | JSON, XML | Protobuf (бинарный) |
| Производительность | Ниже | Выше |
| Размер сообщений | Больше | Меньше |
| Поддержка браузеров | Встроенная | Требует gRPC-Web |
| Типизация | Не строгая | Строгая (Proto) |
| Streaming | Ограниченная | Полная поддержка |
| Использование | Публичные API | Микросервисы, внутренняя коммуникация |
Лучшие практики
1. Используй множественное число для ресурсов
GET /api/users — правильно
GET /api/user — неправильно
GET /api/users/123 — правильно
2. Используй нижний регистр и дефисы
GET /api/user-profiles — правильно
GET /api/userProfiles — неправильно
GET /api/user_profiles — неправильно
3. Используй query-параметры для фильтрации и сортировки
GET /api/users?role=admin&sort=name&order=asc
GET /api/users?page=2&limit=20
4. Возвращай соответствующие коды ответов
POST /api/users → 201 Created
GET /api/users → 200 OK
DELETE /api/users/123 → 204 No Content
GET /api/users/999 → 404 Not Found
5. Используй пагинацию для коллекций
{
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 100,
"totalPages": 5
}
}
6. Поддерживай версионирование
GET /api/v1/users
GET /api/v2/users
Или через заголовки:
Accept: application/vnd.myapi.v1+json
7. Возвращай ошибки в едином формате
{
"error": {
"code": "USER_NOT_FOUND",
"message": "Пользователь не найден",
"details": "Пользователь с id=999 не существует"
}
}
8. Используй HTTPS
Всегда используй HTTPS для REST API. HTTP подходит только для локальной разработки.
9. Документируй API
Используй OpenAPI (Swagger) для документирования:
- Эндпоинты
- Методы
- Параметры
- Схемы данных
- Примеры запросов и ответов
Антипаттерны
1. RPC-стиль в REST
Плохо:
POST /api/getUser
POST /api/createUser
POST /api/deleteUser
Хорошо:
GET /api/users/123
POST /api/users
DELETE /api/users/123
2. Возврат 200 OK для ошибок
Плохо:
HTTP/1.1 200 OK
{
"error": "Пользователь не найден"
}
Хорошо:
HTTP/1.1 404 Not Found
{
"error": "Пользователь не найден"
}
3. Использование GET для изменения состояния
Плохо:
GET /api/users/delete/123
GET /api/users/update?name=НовоеИмя
Хорошо:
DELETE /api/users/123
PUT /api/users/123
4. Отсутствие пагинации
Плохо:
GET /api/users
Возвращает 10 000 пользователей.
Хорошо:
GET /api/users?page=1&limit=20
5. Вложенные ресурсы слишком глубоко
Плохо:
GET /api/users/123/orders/456/items/789
Хорошо:
GET /api/orders/456/items
6. Возврат всех полей всегда
Плохо:
{
"id": 123,
"name": "Иван",
"password": "secret123",
"internalNotes": "...",
"createdAt": "...",
"updatedAt": "..."
}
Хорошо:
{
"id": 123,
"name": "Иван",
"createdAt": "..."
}
Частые заблуждения
Заблуждение 1: «REST — это протокол»
Нет. REST — это архитектурный стиль. HTTP — это протокол. REST может быть реализован поверх HTTP, но не ограничивается им.
Заблуждение 2: «REST и HTTP — одно и то же»
Нет. REST — это стиль архитектуры. HTTP — это протокол. REST использует HTTP, но REST не определяется HTTP.
Заблуждение 3: «REST API должен использовать JSON»
Нет. REST не определяет формат данных. JSON — это просто самый популярный формат. REST может использовать XML, YAML, HTML, или любой другой формат.
Заблуждение 4: «Если API использует HTTP и JSON — это REST»
Нет. Это может быть HTTP API, но не обязательно REST. Для настоящего REST нужно соблюдать все ограничения, включая HATEOAS.
Заблуждение 5: «REST устарел»
Нет. REST остаётся доминирующим стилем для веб-API. Он прост, масштабируем и хорошо поддерживается инструментами.
Заблуждение 6: «RESTful означает полное соответствие REST»
Не всегда. На практике «RESTful» часто означает просто «HTTP API с JSON». Строгое соответствие всем ограничениям REST (включая HATEOAS) встречается редко.
Ключевые тезисы для интервью
- REST — архитектурный стиль, а не протокол. Определён Роем Филдингом в 2000 году.
- Шесть ограничений: клиент-сервер, stateless, кэшируемость, единообразие интерфейса, слоистая система, код по требованию (опционально).
- Ресурс — ключевая абстракция REST. Каждый ресурс идентифицируется уникальным URI.
- HTTP-методы соответствуют CRUD: GET (read), POST (create), PUT (update), PATCH (partial update), DELETE (delete).
- Идемпотентный метод даёт одинаковый результат при многократном вызове. GET, PUT, DELETE — идемпотентны, POST — нет.
- HATEOAS — ответы содержат ссылки на связанные ресурсы и доступные действия. Без HATEOAS API не является REST.
- Коды ответов — часть HTTP, а не REST. REST рекомендует использовать их правильно.
- Большинство API называют «RESTful», но не все строго соответствуют оригинальным принципам Филдинга.
Заключение
REST — архитектурный стиль для проектирования распределённых систем, основанный на шести ограничениях:
- Клиент-серверная архитектура
- Отсутствие состояния
- Кэшируемость
- Единообразие интерфейса
- Слоистая система
- Код по требованию (опционально)
REST, RESTful и REST API — связанные, но разные понятия:
- REST — стиль (теория)
- RESTful — характеристика системы
- REST API — конкретная реализация
Коды ответов — это часть HTTP, а не REST. REST рекомендует использовать их правильно, но не определяет собственных кодов.
Настоящий REST (с HATEOAS) встречается редко, но принципы REST полезны даже для простых HTTP API: правильные методы, коды ответов, ресурсы, кэширование.