Ресурсы, методы и коды состояния

Что это такое

Правила проектирования RESTful API стоят на трёх вещах: называйте вещи, а не действия; используйте методы протокола в том смысле, для которого они определены; сообщайте результат точным кодом состояния. Ресурс — существительное с адресом, метод говорит, что с ним делать, код состояния — что произошло. Последовательное следование этим трём — большая часть того, что делает API приятным в использовании.

Почему это важно

API — интерфейс с долгой жизнью, и именно его соглашения позволяют написать клиента, не читая спецификацию от корки до корки. Когда правила соблюдены, одна и та же форма запроса работает для нового ресурса, и одна и та же обработка ошибок годится для нового сбоя. Однако стоит их нарушить — чтение начинает удалять, создание превращается в чтение, ошибка возвращает код успеха, — и клиентам приходится делать особый случай для каждого эндпоинта; исправлять это дорого, когда API уже в работе.

Ключевые идеи

Называйте ресурсы, а не операции

Адрес должен обозначать вещь, а не действие над ней. Коллекция перечисляет вещи, дочерний адрес обозначает одну из них, вложенный — что-то, что принадлежит другой вещи. Действие, которое не укладывается в метод, лучше смоделировать как подчинённый ресурс, например возврат по платежу, чем добавлять в путь глагол.

GET  /orders            -> список
GET  /orders/42         -> один заказ
POST /orders            -> создание
GET  /orders/42/items   -> позиции одного заказа

Используйте методы в их определённом смысле

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

Коды состояния — машинно-читаемый результат

Успех означает, что запрос понят и выполнен, перенаправление — что смотреть нужно в другом месте, ошибка клиента — что неверен сам запрос и повтор не поможет, ошибка сервера — что запрос может быть верным и повтор может помочь. Конкретный код внутри семейства несёт информацию, по которой клиент способен действовать: ошибка валидации — не то же, что ошибка аутентификации, а она — не то же, что ресурс, которого нет.

Создание возвращает, где теперь живёт вещь

Успешное создание указывает новый ресурс в заголовке и обычно возвращает его представление. Тогда клиент продолжает работу без второго запроса и без угадывания идентификатора. Если вернуть один лишь флаг успеха, каждый клиент начнёт опрашивать сервер или сам конструировать адреса.

Идемпотентность — свойство, которое проектируют, а не совпадение

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

Ошибки должны быть единообразными и содержательными

У каждого сбоя должна быть одна и та же форма: назовите поле или условие, которое не прошло, и добавьте код, по которому клиент ветвится, не разбирая текст. Единообразная форма ошибки означает один обработчик на клиенте, форма для каждого эндпоинта — по одному на эндпоинт. Заодно выравниваются журналы и дашборды: один и тот же код появляется везде.

Практика

Смоделируйте один домен как ресурсы

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

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

Добавьте идемпотентность созданию

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

Готово, когда: отправка одного и того же запроса дважды с тем же ключом даёт одну запись и одинаковые ответы.

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

  • Глаголы в пути — адрес перестаёт обозначать вещь; исправить: смоделируйте операцию как ресурс или используйте правильный метод.
  • Изменение состояния при чтении — обходчики и предзагрузчики выполнят это изменение; исправить: держите чтение безопасным и меняйте состояние методом, который для этого предназначен.
  • Код успеха в ответ на ошибку — каждому клиенту нужен особый случай; исправить: используйте семейство статусов, соответствующее результату.
  • Неповторяемое создание без ключа — повтор создаёт дубликат записи; исправить: принимайте ключ идемпотентности и возвращайте первый результат.
  • Свой формат ошибки на каждый эндпоинт — у клиентов накапливаются парсеры; исправить: одна форма ошибки с кодом для ветвления.

Что читать дальше