Модуль 2 · Правила HTTP

Справочник «Стандартизация HTTP API» · модуль 2 · правила HTTP

Глава 2.1

2.1

REST:
ограничения стиля

REST — архитектурный стиль, а не формат данных и не библиотека. В этой главе разобрано, какие ограничения он накладывает, что каждое из них даёт на практике и почему большинство сервисов, названных «REST API», выполняют их частично.

Категория
Архитектура
Уровень
Базовый
Связанные главы
1.2 Один обмен · 2.2 Методы · 4.3 Заголовки контракта
Зачем это знать
Чтобы понимать, откуда взялись правила про адреса, методы и коды, и когда от них можно отступить

§1Откуда взялся REST

REST описан Роем Филдингом в диссертации 2000 года. Филдинг участвовал в разработке HTTP, и его работа отвечала на вопрос: какие свойства архитектуры сделали Всемирную паутину способной расти десятилетиями без общей перестройки. Ответ он сформулировал как набор ограничений и назвал Representational State Transfer — «передача представления состояния».

ОпределениеАрхитектурный стиль

Набор ограничений на устройство системы. Стиль не говорит, на чём писать и какие библиотеки брать; он говорит, чего система делать не должна, — и за счёт этих запретов приобретает полезные свойства.

Название читается буквально. Клиент не получает объект с сервера — он получает представление его состояния: документ JSON, страницу HTML, изображение. Один и тот же ресурс может иметь несколько представлений, и передаются именно они.

Ограничений в работе Филдинга шесть, и одно из них — необязательное. Ниже разберём каждое и сразу скажем, что оно даёт в обычном рабочем API. Источник — глава 5 диссертации.

§2Клиент и сервер: разные обязанности

Первое ограничение делит систему на две части с разными задачами. Клиент отвечает за представление данных человеку и за навигацию. Сервер — за хранение, изменение и правила доступа.

КлиентСервер
интерфейс и вёрсткабизнес-правила
навигация между экранамихранение данных
черновики и локальные настройкипроверка прав

Практическая ценность — в независимом развитии. Команда мобильного приложения переделывает экран обращения, не трогая сервер. Команда сервиса переносит хранилище с одной СУБД на другую, и приложение об этом не узнаёт. Пока граница между ними описана контрактом, каждая сторона может меняться независимо.

Нарушается это ограничение незаметно. Например, сервер начинает возвращать поле button_color, потому что «так проще менять оформление». С этого момента изменение интерфейса требует релиза сервера — граница размылась.

§3Stateless: запрос самодостаточен

Второе ограничение: сервер не хранит состояние клиента между запросами. Всё, что нужно для обработки, приходит в самом запросе.

каждый защищённый запрос несёт токен
GET /api/v1/tickets HTTP/1.1
Authorization: Bearer demo-token

Противоположный подход — серверная сессия: клиент один раз «входит», сервер запоминает его в памяти и выдаёт номер сессии. Пока сервис запущен в одном экземпляре, это работает. Как только экземпляров становится два, запрос с номером сессии может попасть на тот, который о ней не знает, — и пользователь «разлогинивается» через раз.

Что не запрещает stateless

Ограничение относится к состоянию клиента, а не к данным. Сервер, разумеется, хранит обращения: это состояние ресурсов, а не сеанса. Запрещено другое — ожидать, что клиент уже «сделал шаг 1», не сказав об этом в текущем запросе.

У stateless есть цена: токен и другие сведения путешествуют в каждом запросе, и сообщения становятся немного больше. Взамен любой экземпляр сервиса может обработать любой запрос — отсюда простое горизонтальное масштабирование и безболезненный перезапуск.

§4Кэширование: ответ говорит о себе

Третье ограничение требует, чтобы ответ сообщал, можно ли его повторно использовать. Это делают заголовки.

Cache-ControlПравила повторного использования: max-age=300 разрешает отдавать сохранённый ответ пять минут, no-store запрещает сохранять вовсе.можно ли хранить
ETagВерсия представления. Клиент возвращает её в If-None-Match, и сервер отвечает 304 Not Modified без тела, если ничего не изменилось.изменилось ли

Выигрывают обе стороны: клиент получает ответ быстрее, сервер не тратит время на повторную сборку данных. У GitHub условные запросы вдобавок не расходуют лимит обращений — об этом в главе 4.3.

Опасность тоже есть, и она следует из предыдущих глав: кэш принимает решение по методу и адресу. Если за GET скрыто изменение данных, кэш вправе отдать старый ответ и «отменить» его для пользователя.

§5Слои между клиентом и сервером

Четвёртое ограничение: клиент не должен знать, отвечает ему конечный сервис или запрос прошёл через посредников. Между ними может стоять сеть доставки, шлюз, балансировщик, кэш.

СлойЧто делаетЧто видит клиент
CDNотдаёт сохранённые ответы ближе к пользователютот же адрес, ответ быстрее
API gatewayпроверяет токен, считает лимиты, пишет журнал401 или 429 — как от сервиса
Балансировщикраспределяет запросы между экземпляраминичего: адрес не меняется

Благодаря этому ограничению инфраструктуру меняют без переписывания клиентов. Обратная сторона — диагностика: ответ мог прийти не от вашего кода. Поэтому в контракт добавляют идентификатор запроса (X-Request-Id): он проходит через слои и связывает записи в журналах.

§6Единообразный интерфейс

Пятое ограничение — центральное. Со всеми ресурсами работают по одним и тем же правилам, и правила эти заданы протоколом, а не конкретным сервисом.

URIИдентифицирует ресурс: /api/v1/tickets/{ticket_id}.что
МетодЗадаёт действие: GET, POST, PATCH, DELETE.что сделать
Код состоянияСообщает исход: 200, 201, 404, 412.чем кончилось
ПредставлениеПередаёт состояние ресурса: документ JSON с объявленным Content-Type.что получилось

Отсюда следует правило из главы 1.3: отдельная операция deleteTicket() не нужна, потому что удаление уже выражено методом DELETE. Клиент, умеющий удалять один ресурс, умеет удалять любой.

Единообразие — это про переносимость знания. Разработчик, впервые открывший ваше API, уже знает, что делает GET и что означает 404. Ему остаётся узнать только имена ресурсов.

§7Код по требованию

Шестое ограничение — единственное необязательное. Сервер может передать клиенту исполняемый код, который расширит его возможности. В вебе это происходит постоянно: браузер получает страницу и вместе с ней JavaScript.

В обычных HTTP API это ограничение почти не используют: клиент получает данные, а не программу. Мы упоминаем его, чтобы список был полным, — в контракте Service Desk API ему соответствия нет.

§8«REST API» и HTTP API

На практике словом «REST API» называют почти любое HTTP API, которое использует ресурсы, методы, коды состояния и JSON. Полное соответствие всем ограничениям — прежде всего гипермедиа, когда сервер в каждом ответе сообщает, какие действия доступны дальше, — встречается редко.

Компании обычно фиксируют планку явно. Zalando в своих правилах требует «MUST use REST maturity level 2» — то есть ресурсы, методы и коды состояния — и отдельно допускает «MAY use REST maturity level 3 — HATEOAS» как возможность, а не обязанность.

Как отвечать на собеседовании

Вопрос «ваше API RESTful?» лучше переводить в проверяемую плоскость: какие ограничения выполняются и как это видно. «Ресурсы и методы — да, stateless — да, токен в каждом запросе; кэширование — частично, есть ETag; гипермедиа не используем». Такой ответ точнее, чем «да, у нас REST».

В этой дисциплине проверяется не название стиля, а выполнение отдельных требований. Следующие главы модуля разбирают методы и коды состояния — то, что следует из ограничений напрямую и проверяется тестом.

§9Частые ошибки

«REST — это JSON по HTTP»JSON — формат представления, HTTP — протокол. REST — набор ограничений на архитектуру; можно использовать JSON и нарушать все ограничения сразу.
Сессия в памяти сервисаПервый же второй экземпляр сервиса ломает такую схему. Состояние сеанса передаётся в запросе или хранится в общем хранилище.
Изменение данных методом GETНарушает и единообразие, и кэширование: посредник вправе повторить запрос или отдать сохранённый ответ.
Поля интерфейса в ответе APIЦвет кнопки и порядок вкладок — забота клиента. Как только они приходят с сервера, граница между сторонами исчезает.

§10Проверьте себя

  1. Назовите пять обязательных ограничений REST и скажите, что даёт каждое.
  2. Почему серверная сессия в памяти мешает запустить второй экземпляр сервиса?
  3. Какие заголовки отвечают за повторное использование ответа и чем они отличаются?
  4. Клиент получил 429. Как понять, ответил сервис или шлюз перед ним?
  5. Сформулируйте, каким ограничениям соответствует Service Desk API и каким — нет.
ОграниченияClient–ServerStatelessCacheableLayered SystemUniform InterfaceCode on Demand — опция
Единый интерфейсURI — чтометод — что сделатькод — чем кончилосьпредставление — что вышло
Statelessтокен в каждом запроселюбой экземпляр обслужит
КэшCache-Control: max-ageETag + If-None-Match → 304

Справочник «Стандартизация HTTP API» · модуль 2 · глава 2.1Макаров Максим Николаевич

Кадр