Как правильно именовать сложные действия в REST API?
Добрый день!
Во всех руководствах в описаниях REST дают простые примеры, типа вот вам пользователи, они будут ресурсом /users, вот вам один пользователь, он будет /users/[id] и действия с ним добавить\удалить\изменить.
Предположим, пишем приложение "ядерный чемоданчик" с REST интерфейсом. Функционал: 1. Действия с ракетой [id]: запуск по стране, продажа в страну, обслуживание, списать в утиль
По смыслу используемый ресурс /missiles/[id], но указанные действия "в лоб" ни в один из стандартных HTTP методов не вписываются.
Есть вариант расширять список стандартных HTTP-методов, но какой-то он идеологически не правильный. Плюс могут быть сложности с поддержкой у разных клиентов и готовых библиотек.
Делать /missiles/[id]?action=launch лишает смысла весь REST подход, т.к. бОльшая часть функционала будет в действиях.
Делать /missiles/[id]/launch - тащим глагол в адрес ресурса - тоже неправильно.
2. Запустить ракету [id] по стране [country_id].
С некоторой долей юмора можно сделать PATCH или LINK /missile/[id]/country/[id], но это разовое действие, а не постоянная связь.
Опять же, есть несколько похожих действий (продать в страну или запустить по стране), т.е. будет неочевидно что из них что ну и количество стандартных методов ограничено.
Можно сделать действие "быть раздолбанным" для ресурса /countries/[id], но это по смыслу бредово.
3. Запустить любую подходящую ракету по стране [country_id].
Предположим, что ракет очень дофига и делать сначала поиск, а потом запуск накладно и долго, нужен один запрос на моментальное исполнение.
Смысл что [id] конкретной ракеты мы не знаем, выполнять действие "запуск" на всю коллекцию ракет некорректно.
4. Действия не имеющие ресурса
Есть например действие "провести парад". Никаких связанных ресурсов нет, только глагол.
Тащить его в URL /start_parade ?
В идеологии REST нужно мыслить с точки зрения декомпозиции предметной области задачи на объекты-ресурсы (с соответствующими урлам), а глаголы (действия) строго фиксированные (закреплённые, в том числе, в протоколе HTTP). Этим достигается масштабируемость (во всех смыслах) разрабатываемого веб-сервиса. Если в прикладной области видится большое разнообразие действий, а не объектов, то их можно воспринимать в стиле документов документооборота или транзакций платёжной системы. Например, пуск ракеты по стране можно представить в виде создания документа (транзакции) "пуск ракеты по стране". Оформить можно и как вложенный в ракету ресурс, и как корневой ресурс с атрибутами "ракета" и "страна", - это зависит от физической и логической организации сервиса. Такой ресурс-документ можно снабдить идентификатором и дать ему возможность обрабатываться на любом сервере, входящем в инфраструктуру веб-сервиса, читать из него автоматически сгенерированные атрибуты типа "время запуска", "статус полёта", т.п. В общем, основная идея комментария: мыслить в терминах документооборота, и сервис получится логичным, поддерживаемым, масштабируемым, как и задумано REST'ом.
Много где читал, да и сам придерживаюсь использовать в нестандартных запросах - параметры.
Опять же нужно определить основной ресурс с которым идет работа, в вашем примере скорее всего это ракета.
У этого ресурса может быть состояние или действие (зависит от архитектуры, что лучше подходит).
Пример с ракетой у которой меняем состояние: (В этом случае у таблицы с ракетами должно быть поле target)
PUT rocket/23/?launch {'target': 'usa'}
Пример с действиями подразумевает более широкие возможности по использованию ракет. В данном примере должна быть отдельная таблица rocket_action с полями: действие, состояние, цель ...
Запрос на продажу ракеты:
POST rocket/23/?sale {'target': 'pakistan', 'price': 235000};
(соответственно контроллер, который отвечает за обработку этого действия должен удалить ракету из таблицы ракет или изменить её состояние)
Что же касается действий под пунктами 3 и 4:
3 - Такие действия должны лучше разбивать на поддействия: 1. запросить какая ракета свободна (или ближе всех к этой стране) 2. запустить эту ракету в страну.
4 - думаю что тут нужно пересматривать архитектуру приложения, если нет сущности, с которым мы можем выполнять действие. Для вашего примера лучше подойдет выделение отдельной ракеты для парада POST rocket/23/?start_parad {'place': 'Moscow'}
Да и вообще REST это довольно абстрактная архитектура. Тут в первую очередь нужно думать о том, что хочет получить пользователь отправляя этот запрос, а уже потом на сколько он логичен в техническом плане построенной архитектуры.
Использование различных пользовательских параметров для осуществления действий совершенно не соответствует идеологии REST (несмотря на всю её абстрактность у неё есть вполне конкретные практические предпосылки), чревато проблемами будущей поддержки и масштабирования. В урле должен быть адрес _ресурса_, а действие с ресурсом должно выражаться стандартным HTTP-глаголом по работе с ресурсом. Тогда и поведение браузера будет логичным (отличие GET от POST там не просто так), и коды ошибок можно использовать стандартные (которые тоже придуманы исходя из "ресурсной" терминологии/идеологии), и механизмы кеширования будут задействованы правильно (т.к. менять объекты могут только "избранные" глаголы, а урл строго соответствует ресурсу вне зависимости от осуществляемого действия), и документация API будет гораздо компактнее (т.к. не будет описывать новые логики относительно REST). Если нужны нестандартные действия - вырази их в форме новых подчинённых ресурсов (например, представляя их "квитанциями об операции", в стиле документооборота), с которыми работают всё те же стандартные глаголы REST.
Все imho
1) POST /missiles/[id]/launch мне кажется вполне нормальным
2) POST /missile/[id]/country/[id] мне кажется вполне нормальным
3) POST /country/[id]/destroy ;)
4) POST /parade/start
azverin: Указываются не конкретные действия, а их влияние на данные: GET не меняет, PUT добавляет новый, POST изменяет существующий, DELETE удаляет. А действие вполне правильно передавать в пути.
FanatPHP: я не ругаюсь на объект. Я говорю что действия, которые планируется выполнять не вписываются ни в один из стандартных HTTP-методов.
Читай фразу как "по смыслу [используемый] ресурс..."
azverin: Использование различных пользовательских параметров для осуществления действий совершенно не соответствует идеологии REST (несмотря на всю её абстрактность у неё есть вполне конкретные практические предпосылки), чревато проблемами будущей поддержки и масштабирования. В урле должен быть адрес _ресурса_, а действие с ресурсом должно выражаться стандартным HTTP-глаголом по работе с ресурсом. Тогда и поведение браузера будет логичным (отличие GET от POST там не просто так), и коды ошибок можно использовать стандартные (которые тоже придуманы исходя из "ресурсной" терминологии/идеологии), и механизмы кеширования будут задействованы правильно (т.к. менять объекты могут только "избранные" глаголы, а урл строго соответствует ресурсу вне зависимости от осуществляемого действия), и документация API будет гораздо компактнее (т.к. не будет описывать новые логики относительно REST).
Если нужны нестандартные действия - вырази их в форме новых подчинённых ресурсов (например, представляя их "квитанциями об операции", в стиле документооборота), с которыми работают всё те же стандартные глаголы REST.