Ресурсы, методы и коды состояния
Что это такое
Правила проектирования RESTful API стоят на трёх вещах: называйте вещи, а не действия; используйте методы протокола в том смысле, для которого они определены; сообщайте результат точным кодом состояния. Ресурс — существительное с адресом, метод говорит, что с ним делать, код состояния — что произошло. Последовательное следование этим трём — большая часть того, что делает API приятным в использовании.
Почему это важно
API — интерфейс с долгой жизнью, и именно его соглашения позволяют написать клиента, не читая спецификацию от корки до корки. Когда правила соблюдены, одна и та же форма запроса работает для нового ресурса, и одна и та же обработка ошибок годится для нового сбоя. Однако стоит их нарушить — чтение начинает удалять, создание превращается в чтение, ошибка возвращает код успеха, — и клиентам приходится делать особый случай для каждого эндпоинта; исправлять это дорого, когда API уже в работе.
Ключевые идеи
Называйте ресурсы, а не операции
Адрес должен обозначать вещь, а не действие над ней. Коллекция перечисляет вещи, дочерний адрес обозначает одну из них, вложенный — что-то, что принадлежит другой вещи. Действие, которое не укладывается в метод, лучше смоделировать как подчинённый ресурс, например возврат по платежу, чем добавлять в путь глагол.
GET /orders -> список
GET /orders/42 -> один заказ
POST /orders -> создание
GET /orders/42/items -> позиции одного заказа
Используйте методы в их определённом смысле
Чтение безопасно и повторяемо, создание — нет, замена всей вещи по заданному адресу повторяема, удаление повторяемо по результату. На этих значениях держатся посредники, повторные попытки и предзагрузчики: эндпоинт, меняющий состояние при чтении, сработает из-за предпросмотра ссылки или обходчика, а тот, что создаёт вторую запись при повторе, будет плодить дубликаты при каждом сбое сети.
Коды состояния — машинно-читаемый результат
Успех означает, что запрос понят и выполнен, перенаправление — что смотреть нужно в другом месте, ошибка клиента — что неверен сам запрос и повтор не поможет, ошибка сервера — что запрос может быть верным и повтор может помочь. Конкретный код внутри семейства несёт информацию, по которой клиент способен действовать: ошибка валидации — не то же, что ошибка аутентификации, а она — не то же, что ресурс, которого нет.
Создание возвращает, где теперь живёт вещь
Успешное создание указывает новый ресурс в заголовке и обычно возвращает его представление. Тогда клиент продолжает работу без второго запроса и без угадывания идентификатора. Если вернуть один лишь флаг успеха, каждый клиент начнёт опрашивать сервер или сам конструировать адреса.
Идемпотентность — свойство, которое проектируют, а не совпадение
Повторяемую операцию можно безопасно повторить после тайм-аута, неповторяемую — нельзя. Создания, которые приходится повторять, — платёж, заказ, — клиент снабжает ключом, а сервер этот ключ записывает, поэтому вторая попытка с тем же ключом возвращает первый результат, а не выполняет работу дважды. Именно этим система, переживающая нестабильную сеть, отличается от системы, списывающей деньги дважды.
Ошибки должны быть единообразными и содержательными
У каждого сбоя должна быть одна и та же форма: назовите поле или условие, которое не прошло, и добавьте код, по которому клиент ветвится, не разбирая текст. Единообразная форма ошибки означает один обработчик на клиенте, форма для каждого эндпоинта — по одному на эндпоинт. Заодно выравниваются журналы и дашборды: один и тот же код появляется везде.
Практика
Смоделируйте один домен как ресурсы
Возьмите небольшой домен с тремя существительными и одной операцией, которая не укладывается в метод, и выпишите адреса и методы для каждого.
Готово, когда: каждый адрес обозначает вещь, каждый метод соответствует своему определённому смыслу, а необычная операция имеет собственный ресурс.
Добавьте идемпотентность созданию
Возьмите эндпоинт создания и добавьте ключ, который передаёт клиент: тогда повторный запрос вернёт исходный результат, а не создаст вторую запись.
Готово, когда: отправка одного и того же запроса дважды с тем же ключом даёт одну запись и одинаковые ответы.
Частые ошибки
- Глаголы в пути — адрес перестаёт обозначать вещь; исправить: смоделируйте операцию как ресурс или используйте правильный метод.
- Изменение состояния при чтении — обходчики и предзагрузчики выполнят это изменение; исправить: держите чтение безопасным и меняйте состояние методом, который для этого предназначен.
- Код успеха в ответ на ошибку — каждому клиенту нужен особый случай; исправить: используйте семейство статусов, соответствующее результату.
- Неповторяемое создание без ключа — повтор создаёт дубликат записи; исправить: принимайте ключ идемпотентности и возвращайте первый результат.
- Свой формат ошибки на каждый эндпоинт — у клиентов накапливаются парсеры; исправить: одна форма ошибки с кодом для ветвления.
Что читать дальше
- RESTful API (05-backend-data/06-restful-api) — стиль, которому принадлежат эти правила, поэтому начните с него.
- Версионирование, пагинация и ответы N+1 (05-backend-data/06-restful-api/02-versioning-pagination-and-n-plus-one) — как эти правила выглядят, когда появляются клиенты и объём данных.
- Проектирование API (OpenAPI / Swagger) (05-backend-data/09-api-design-openapi-swagger) — как зафиксировать эти решения в спецификации до начала работы.