Справочник «Стандартизация HTTP API» · модуль 2 · правила HTTP
Глава 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
Противоположный подход — серверная сессия: клиент один раз «входит», сервер запоминает его в памяти и выдаёт номер сессии. Пока сервис запущен в одном экземпляре, это работает. Как только экземпляров становится два, запрос с номером сессии может попасть на тот, который о ней не знает, — и пользователь «разлогинивается» через раз.
Ограничение относится к состоянию клиента, а не к данным. Сервер, разумеется, хранит обращения: это состояние ресурсов, а не сеанса. Запрещено другое — ожидать, что клиент уже «сделал шаг 1», не сказав об этом в текущем запросе.
У stateless есть цена: токен и другие сведения путешествуют в каждом запросе, и сообщения становятся немного больше. Взамен любой экземпляр сервиса может обработать любой запрос — отсюда простое горизонтальное масштабирование и безболезненный перезапуск.
§4Кэширование: ответ говорит о себе
Третье ограничение требует, чтобы ответ сообщал, можно ли его повторно использовать. Это делают заголовки.
max-age=300 разрешает отдавать сохранённый ответ пять минут, no-store запрещает сохранять вовсе.можно ли хранитьIf-None-Match, и сервер отвечает 304 Not Modified без тела, если ничего не изменилось.изменилось лиВыигрывают обе стороны: клиент получает ответ быстрее, сервер не тратит время на повторную сборку данных. У GitHub условные запросы вдобавок не расходуют лимит обращений — об этом в главе 4.3.
Опасность тоже есть, и она следует из предыдущих глав: кэш принимает решение по методу и адресу. Если за GET скрыто изменение данных, кэш вправе отдать старый ответ и «отменить» его для пользователя.
§5Слои между клиентом и сервером
Четвёртое ограничение: клиент не должен знать, отвечает ему конечный сервис или запрос прошёл через посредников. Между ними может стоять сеть доставки, шлюз, балансировщик, кэш.
| Слой | Что делает | Что видит клиент |
|---|---|---|
| CDN | отдаёт сохранённые ответы ближе к пользователю | тот же адрес, ответ быстрее |
| API gateway | проверяет токен, считает лимиты, пишет журнал | 401 или 429 — как от сервиса |
| Балансировщик | распределяет запросы между экземплярами | ничего: адрес не меняется |
Благодаря этому ограничению инфраструктуру меняют без переписывания клиентов. Обратная сторона — диагностика: ответ мог прийти не от вашего кода. Поэтому в контракт добавляют идентификатор запроса (X-Request-Id): он проходит через слои и связывает записи в журналах.
§6Единообразный интерфейс
Пятое ограничение — центральное. Со всеми ресурсами работают по одним и тем же правилам, и правила эти заданы протоколом, а не конкретным сервисом.
/api/v1/tickets/{ticket_id}.чтоGET, POST, PATCH, DELETE.что сделать200, 201, 404, 412.чем кончилось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Частые ошибки
§10Проверьте себя
- Назовите пять обязательных ограничений REST и скажите, что даёт каждое.
- Почему серверная сессия в памяти мешает запустить второй экземпляр сервиса?
- Какие заголовки отвечают за повторное использование ответа и чем они отличаются?
- Клиент получил
429. Как понять, ответил сервис или шлюз перед ним? - Сформулируйте, каким ограничениям соответствует Service Desk API и каким — нет.
Client–ServerStatelessCacheableLayered SystemUniform InterfaceCode on Demand — опцияURI — чтометод — что сделатькод — чем кончилосьпредставление — что вышлотокен в каждом запроселюбой экземпляр обслужитCache-Control: max-ageETag + If-None-Match → 304Справочник «Стандартизация HTTP API» · модуль 2 · глава 2.1Макаров Максим Николаевич