Навигация

REST — Архитектурный стиль для распределённых систем

REST определяет, как должны взаимодействовать компоненты распределённой системы. Это не протокол и не фреймворк, а набор архитектурных ограничений, формализованных Роем Филдингом в 2000 году. Понимание этих принципов помогает проектировать API, которые масштабируются, кэшируются и легко эволюционируют.

Содержание

  1. Что такое REST
  2. История появления
  3. REST, RESTful и REST API — одно и то же?
  4. Ограничения и принципы REST
  5. Ресурсы — ключевая абстракция
  6. HTTP-методы и их семантика
  7. Коды ответов: HTTP или REST?
  8. HATEOAS
  9. Примеры запросов
  10. REST vs другие подходы
  11. Лучшие практики
  12. Антипаттерны
  13. Частые заблуждения

Что такое 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Описание
POSTCreateСоздание нового ресурса
GETReadЧтение ресурса
PUTUpdateПолное обновление ресурса
PATCHUpdateЧастичное обновление ресурса
DELETEDeleteУдаление ресурса

Коды ответов: 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

ХарактеристикаRESTSOAP
ПротоколHTTPSOAP поверх HTTP/SMTP/TCP
Формат данныхJSON, XML, и др.Только XML
СложностьПростойСложный
ПроизводительностьБыстрееМедленнее
КэшированиеВстроенное (HTTP)Требует дополнительной реализации
ТипизацияНе строгаяСтрогая (WSDL)
ИспользованиеВеб-API, мобильные приложенияКорпоративные системы, банкинг

REST vs GraphQL

ХарактеристикаRESTGraphQL
ЗапросФиксированные эндпоинтыГибкий язык запросов
ОтветФиксированная структураКлиент определяет структуру
Over-fetchingВозможнаИсключена
Under-fetchingВозможнаИсключена
КэшированиеПростое (HTTP)Требует дополнительной реализации
СложностьПростойСложнее
ВерсионированиеЧасто требуетсяНе требуется
ИспользованиеПростые API, CRUDСложные данные, гибкие запросы

REST vs gRPC

ХарактеристикаRESTgRPC
ПротоколHTTP/1.1 или HTTP/2HTTP/2
Формат данныхJSON, XMLProtobuf (бинарный)
ПроизводительностьНижеВыше
Размер сообщенийБольшеМеньше
Поддержка браузеровВстроеннаяТребует 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 — архитектурный стиль для проектирования распределённых систем, основанный на шести ограничениях:

  1. Клиент-серверная архитектура
  2. Отсутствие состояния
  3. Кэшируемость
  4. Единообразие интерфейса
  5. Слоистая система
  6. Код по требованию (опционально)

REST, RESTful и REST API — связанные, но разные понятия:

  • REST — стиль (теория)
  • RESTful — характеристика системы
  • REST API — конкретная реализация

Коды ответов — это часть HTTP, а не REST. REST рекомендует использовать их правильно, но не определяет собственных кодов.

Настоящий REST (с HATEOAS) встречается редко, но принципы REST полезны даже для простых HTTP API: правильные методы, коды ответов, ресурсы, кэширование.

Полезные ссылки