В этой статье автор утверждает, что
Иногда требуется представить в API операцию, которая по своей природе не является RESTful.
и это
Если API имеет слишком много действий, то это указывает на то, что он был разработан с точки зрения RPC, а не с использованием принципов RESTful, или что данный API, естественно, лучше подходит для модели типа RPC.
Это отражает то, что я читал и слышал и в других местах.
Однако я нахожу это довольно запутанным, и я хотел бы получить лучшее понимание вопроса.
Пример I: Завершение работы виртуальной машины через интерфейс REST
Я думаю, что есть два принципиально разных способа моделирования выключения виртуальной машины. У каждого способа может быть несколько вариантов, но давайте сосредоточимся на самых фундаментальных различиях на данный момент.
1. Заплатить государственную собственность ресурса
PATCH /api/virtualmachines/42
Content-Type:application/json
{ "state": "shutting down" }
(В качестве альтернативы PUT
на подресурсе /api/virtualmachines/42/state
.)
Виртуальная машина будет выключаться в фоновом режиме, и в какой-то более поздний момент времени, в зависимости от того, будет ли завершение работы системы завершено успешно, или нет, состояние может быть внутренне обновлено с помощью «выключения».
2. PUT или POST для свойства действия ресурса
PUT /api/virtualmachines/42/actions
Content-Type:application/json
{ "type": "shutdown" }
Результат точно такой же, как в первом примере. Состояние будет обновлено до «выключения» немедленно и, возможно, в конечном итоге до «выключения».
Оба дизайна RESTful?
Какой дизайн лучше?
Пример II: CQRS
Что если у нас есть домен CQRS со многими такими «действиями» (или командами), которые потенциально могут привести к обновлениям нескольких агрегатов или не могут быть сопоставлены с операциями CRUD на конкретных ресурсах и подресурсах?
Должны ли мы пытаться моделировать столько команд, сколько конкретных создает или обновляет конкретные ресурсы, где это возможно (следуя первому подходу из примера I), и использовать «конечные точки действия» для остальных?
Или мы должны сопоставить все команды конечным точкам действия (как во втором подходе примера I)?
Где мы должны провести черту? Когда дизайн становится менее RESTful?
Модель CQRS лучше подходит для API, подобного RPC?
Согласно приведенному выше тексту, как я понимаю.
Как вы можете видеть из моих многочисленных вопросов, я немного запутался в этой теме. Можете ли вы помочь мне лучше понять это?
источник
Ответы:
В первом случае (закрытие виртуальных машин) я бы не рассмотрел ни одну из альтернатив OP Rfulful. Конечно, если вы используете модель зрелости Ричардсона в качестве критерия, они оба являются API уровня 2, потому что они используют ресурсы и глаголы.
Однако ни один из них не использует элементы управления гипермедиа, и, на мой взгляд, это единственный тип REST, который отличает дизайн RESTful API от RPC. Другими словами, придерживайтесь уровня 1 и 2, и в большинстве случаев у вас будет API в стиле RPC.
Чтобы смоделировать два различных способа выключения виртуальной машины, я бы выставил саму виртуальную машину как ресурс, который (помимо прочего) содержит ссылки:
Если клиент желает завершить работу
Ploeh
виртуальной машины, он должен перейти по ссылке сshut-down
типом отношения. (Обычно, как указано в RESTful Web Services Cookbook , вы бы использовали IRI или более сложную схему идентификации для типов отношений, но я решил сохранить пример как можно более простым.)В этом случае есть немного другой информации, чтобы предоставить с действием, поэтому клиент должен просто сделать пустой POST для URL в
href
:(Поскольку у этого запроса нет тела, было бы заманчиво смоделировать его как запрос GET, но запросы GET не должны иметь видимых побочных эффектов, поэтому POST более корректен.)
Аналогичным образом, если клиент хочет отключить виртуальную машину, он перейдет по
power-off
ссылке.Другими словами, типы отношений ссылок предоставляют возможности, которые указывают на намерение. Каждый тип отношений имеет определенное семантическое значение. Вот почему мы иногда говорим о семантической паутине .
Чтобы пример был как можно более понятным, я намеренно скрыл URL-адреса в каждой ссылке. Когда хостинг - сервер получает входящий запрос, он будет знать , что
fdaIX
средства закрыты , иCHTY91
средство отключения питания .Обычно я просто кодирую действие в самом URL, чтобы URL были
/vms/1234/shut-down
и/vms/1234/power-off
, но при обучении это стирает различие между типами отношений (семантика) и URL (подробности реализации).В зависимости от того, какие клиенты у вас есть, вы можете сделать URL-адреса RESTful не взломанными .
CQRS
Когда дело доходит до CQRS, Грег Янг и Уди Дахан соглашаются с тем, что CQRS не является архитектурой верхнего уровня . Поэтому я бы с осторожностью относился к тому, чтобы сделать RESTful API слишком похожим на CQRS, потому что это означало бы, что клиенты становятся частью вашей архитектуры.
Часто движущей силой настоящего (уровня 3) API RESTful является то, что вы хотите иметь возможность развивать свой API, не нарушая клиентов и не имея контроля над клиентами. Если это ваша мотивация, то CQRS не будет моим первым выбором.
источник
DELETE
мне кажется странным, потому что после выключения виртуальная машина все еще будет существовать, только в состоянии «выключить» (или что-то в этом роде).Завершение работы виртуальной машины через интерфейс REST
На самом деле это довольно известный пример, выдвинутый Тимом Бреем в 2009 году .
Рой Филдинг, обсуждая проблему, поделился этим наблюдением :
Вкратце, у вас есть один информационный ресурс, который возвращает текущее представление отслеживаемого состояния; это представление может включать гипермедиа ссылку на форму, которая запрашивает изменение в этом состоянии, а форма имеет другую ссылку на ресурс для обработки (каждого) запроса на изменение.
Сет Лэдд имел ключевое понимание проблемы
RESTful-программирование - это бюрократия Вогона в веб-масштабе. Как вы делаете что-нибудь RESTful? Придумайте новые документы для этого и оцифруйте документы.
На более изящном языке вы определяете протокол приложения домена для «выключения виртуальной машины» и определяете ресурсы, необходимые для предоставления / реализации этого протокола.
Глядя на свои примеры
Ничего страшного; вы на самом деле не рассматриваете сам запрос как отдельный отдельный информационный ресурс, но вы все равно можете управлять им.
Вы немного пропустили свое представление об изменениях.
Например, инструкции JSON Patch форматируют инструкции, как если бы вы непосредственно изменяли документ JSON
В вашей альтернативе идея близка, но не совсем верна.
PUT
является полной заменой состояния ресурса по целевому URL , поэтому вы, вероятно, не выберете орфографию, которая выглядит как коллекция, как цель представления одного объекта.Соответствует выдумке, что мы добавляем действие в очередь
Это согласуется с выдумкой о том, что мы делаем обновление для хвостового элемента в очереди; это немного странно делать это таким образом. Принцип наименьшего удивления рекомендует присваивать каждому PUT свой уникальный идентификатор, а не размещать их все в одном месте и модифицировать несколько ресурсов одновременно.
Обратите внимание, что, поскольку мы обсуждаем написание URI - REST не волнует;
/cc719e3a-c772-48ee-b0e6-09b4e7abbf8b
является совершенно громоздким URI для REST. Читаемость, как и с именами переменных, является отдельной проблемой. Использование написания, соответствующего RFC 3986 , сделает людей намного счастливее.CQRS
Грег Янг о CQRS
Учитывая, что вы говорите о CQRS в контексте HTTP / REST, кажется разумным предположить, что вы работаете в этом последнем контексте, так что давайте продолжим.
Этот, на удивление, даже проще, чем ваш предыдущий пример. Причина этого проста: команды - это сообщения .
Джим Уэббер описывает HTTP как протокол приложений офиса 1950-х годов; работа выполняется путем приема сообщений и помещения их в почтовые ящики. Та же идея верна - мы получаем чистую копию формы, заполняем ее известной нам спецификой, доставляем ее. Та да
Да, поскольку «конкретные ресурсы» - это сообщения, а не сущности в доменной модели.
Основная идея: ваш REST API все еще является интерфейсом ; Вы должны быть в состоянии изменить базовую модель без необходимости изменения сообщений клиентами. Когда вы выпускаете новую модель, вы выпускаете новую версию веб-конечных точек, которые знают, как использовать протокол вашего домена и применять его к новой модели.
Не совсем - в частности, веб-кеши являются отличным примером «в конечном итоге согласованной модели чтения». Делая каждое из ваших представлений независимо адресуемыми, каждое со своими правилами кэширования, вы получаете бесплатное масштабирование. Относительно малообращенный подход к чтению RPC относительно невелик.
Для записи это сложный вопрос: отправка всех команд одному обработчику в одной конечной точке или в одном семействе конечных точек, безусловно, проще . REST на самом деле больше о том, как вы общаетесь, где конечная точка находится для клиента.
Преимущество обработки сообщения как его собственного уникального ресурса заключается в том, что вы можете использовать PUT, предупреждая промежуточные компоненты о том, что обработка сообщения идемпотентна, так что они могут участвовать в определенных случаях обработки ошибок. , (Обратите внимание: с точки зрения клиентов, если ресурсы имеют разные URI, то они являются разными ресурсами; тот факт, что все они могут иметь одинаковый код обработчика запросов на исходном сервере, является подробностью реализации, скрытой униформой интерфейс).
Филдинг (2008)
источник
Вы можете использовать 5 уровней типа мультимедиа, чтобы указать команду в поле заголовка типа содержимого запроса.
В примере с VM это будет что-то вроде этого
затем
Или
Смотрите https://www.infoq.com/articles/rest-api-on-cqrs
источник
Пример в связанной статье основан на идее, что запуск машины и ее выключение должны выполняться командами, а не изменениями состояния моделируемых ресурсов. Последнее в значительной степени то, что отдыхает и дышит. Лучшее моделирование виртуальной машины требует взгляда на то, как работает ее реальный аналог и как вы, как человек, будете взаимодействовать с ней. Это многословно, но я думаю, что это дает хорошее представление о типе мышления, необходимого для хорошего моделирования.
Есть два способа управления состоянием питания компьютера на моем столе:
Для виртуальной машины оба они могут быть смоделированы как логические значения для чтения / записи:
power
- При изменении наtrue
ничего не происходит, кроме того, что делается пометка о том, что переключатель переведен в это состояние. При переключенииfalse
на ВМ вводится в состояние немедленного отключения. Для полноты, если значение не изменилось после записи, ничего не происходит.onoff
- При изменении наtrue
ничего не происходит, еслиpower
естьfalse
, в противном случае виртуальная машина получает команду на запуск. При изменении наfalse
ничего не происходит, еслиpower
естьfalse
, в противном случае виртуальная машина получает команду уведомить программное обеспечение о необходимости упорядоченного завершения работы, а затем уведомить виртуальную машину о том, что она может перейти в состояние отключения питания. Опять же, для полноты, запись без изменений ничего не делает.Со всем этим приходит понимание того, что существует одна ситуация, когда состояние машины не отражает состояние переключателей, и это происходит во время выключения.
power
все еще будетtrue
иonoff
будетfalse
, но процессор все еще работает, и для этого нам нужно добавить другой ресурс, чтобы мы могли сказать, что на самом деле делает машина:running
- Значение только для чтения,true
когда виртуальная машина работает, аfalse
когда нет, определяется путем запроса гипервизора о ее состоянии.Результатом этого является то, что если вы хотите, чтобы виртуальная машина запускалась, вы должны убедиться, что
power
иonoff
ресурсы установленыtrue
. (Вы можете разрешитьpower
пропуск шага, сделав его самовозвратом, так что если он установлен в значениеfalse
, то он становитсяtrue
после того, как виртуальная машина была проверенно проверена. Если это RESTly-чистая вещь, которую нужно сделать, это корм для другого обсуждения.) Если вы хотите, чтобы это сделать нормальное завершение работы, вы установилиonoff
вfalse
и вернуться позже , чтобы увидеть , если машина фактически остановилась, установкаpower
дляfalse
если это не так.Как и в реальном мире, у вас все еще есть проблема с указанием запустить виртуальную машину после того, как она была
onoff
изменена,false
но все ещеrunning
потому, что она находится в середине выключения. То, как вы справляетесь с этим, является политическим решением.источник
Так что если вы хотите думать спокойно, забудьте о командах. Клиент не говорит серверу выключить виртуальную машину. Клиент «закрывает» (в переносном смысле) свою копию представления ресурса, обновляя его состояние, а затем помещает это представление обратно на сервер. Сервер принимает это новое представление состояния и, в качестве побочного эффекта, фактически выключает виртуальную машину. Побочный эффект оставлен на усмотрение сервера.
Это меньше
Эй, сервер, клиент здесь, не могли бы вы закрыть виртуальную машину
и многое другое
Эй, сервер, клиент здесь, я обновил состояние виртуальной машины 42 до состояния выключения, обновил вашу копию этого ресурса и затем сделал то, что вы считаете подходящим
В качестве побочного эффекта сервера, принимающего это новое состояние, он может проверить, какие действия он фактически должен выполнить (например, физически завершить работу виртуальной машины 42), но это прозрачно для клиента. Клиент не заботится о каких-либо действиях, которые должен предпринять сервер, чтобы соответствовать этому новому состоянию
Так что забудьте о командах. Единственными командами являются глаголы в HTTP для передачи состояния. Клиент не знает и не заботится о том, как сервер переведет физическую виртуальную машину в состояние выключения. Клиент не выдает команды серверу для достижения этой цели, он просто говорит, что это новое состояние, разберись .
Сила этого в том, что он отделяет клиента от сервера с точки зрения управления потоком. Если позже сервер изменит свою работу с виртуальными машинами, клиенту все равно. Нет конечных точек команд для обновления. В RPC, если вы измените конечную точку API с
shutdown
на,shut-down
вы сломали все свои клиенты, так как они теперь не знают команду для вызова на сервере.REST похож на декларативное программирование, где вместо того, чтобы перечислять набор инструкций для изменения чего-либо, вы вместо этого просто указываете, как вы хотите, чтобы это было, и позволяете среде программирования это понять.
источник
POST /api/virtualmachines/42/shutdown
вместо того, чтобы иметь какой-либо "побочный эффект". API должен быть понятен пользователю. Как я узнаю, что, напримерDELETE /api/virtualmachines/42
, виртуальная машина отключится? Побочным эффектом для меня является ошибка, мы должны спроектировать наши API так, чтобы они были понятными и информативными