Мне интересно, как бы вы реализовали следующий вариант использования в REST. Можно ли вообще обойтись без ущерба для концептуальной модели?
Прочитайте или обновите несколько ресурсов в рамках одной транзакции. Например, переведите 100 долларов с банковского счета Боба на счет Джона.
Насколько я могу судить, единственный способ реализовать это - обман. Вы можете отправить POST к ресурсу, связанному с Джоном или Бобом, и выполнить всю операцию, используя одну транзакцию. Насколько мне известно, это нарушает архитектуру REST, потому что вы, по сути, туннелируете вызов RPC через POST, а не работаете с отдельными ресурсами.
Есть несколько важных случаев, на которые этот вопрос не получил ответа, что я считаю слишком плохим, потому что он имеет высокий рейтинг в Google по поисковым запросам :-)
В частности, было бы неплохо: если вы выполняете POST дважды (из-за того, что некоторый кеш работает в промежуточном состоянии), вы не должны передавать сумму дважды.
Чтобы добраться до этого, вы создаете транзакцию как объект. Он может содержать все данные, которые вы уже знаете, и переводить транзакцию в состояние ожидания.
Как только у вас есть эта транзакция, вы можете зафиксировать ее, например:
Обратите внимание, что множественные путы на данном этапе не имеют значения; даже GET на TXN вернет текущее состояние. В частности, второй PUT обнаружит, что первый уже находится в соответствующем состоянии, и просто вернет его - или, если вы попытаетесь перевести его в состояние «отката» после того, как он уже находится в «зафиксированном» состоянии, вы получите ошибка, и фактическая совершенная транзакция возвращается.
Пока вы общаетесь с одной базой данных или базой данных со встроенным монитором транзакций, этот механизм на самом деле будет работать просто отлично. Вы могли бы дополнительно ввести тайм-ауты для транзакций, которые вы могли бы даже выразить, используя заголовки Expires, если хотите.
источник
В терминах REST ресурсы - это существительные, на которые можно воздействовать с помощью глаголов CRUD (создание / чтение / обновление / удаление). Поскольку глагола «перевод денег» не существует, нам необходимо определить ресурс «транзакции», на который можно воздействовать с помощью CRUD. Вот пример в HTTP + POX. Первый шаг - СОЗДАНИЕ (метод HTTP POST) новой пустой транзакции:
Это возвращает идентификатор транзакции, например, «1234» и в соответствии с URL «/ транзакция / 1234». Обратите внимание, что запуск этого POST несколько раз не приведет к созданию одной и той же транзакции с несколькими идентификаторами, а также позволит избежать введения состояния ожидания. Кроме того, POST не всегда может быть идемпотентным (требование REST), поэтому обычно рекомендуется минимизировать данные в POST.
Вы можете оставить генерацию идентификатора транзакции клиенту. В этом случае вам потребуется POST / транзакция / 1234 для создания транзакции «1234», и сервер вернет ошибку, если она уже существует. В ответе об ошибке сервер может вернуть текущий неиспользуемый идентификатор с соответствующим URL-адресом. Не следует запрашивать у сервера новый идентификатор с помощью метода GET, поскольку GET никогда не должен изменять состояние сервера, а создание / резервирование нового идентификатора изменяет состояние сервера.
Далее мы ОБНОВЛЯЕМ (метод PUT HTTP) транзакцию со всеми данными, неявно фиксируя ее:
Если транзакция с идентификатором «1234» ранее была PUT, сервер выдает ответ об ошибке, в противном случае - ответ OK и URL-адрес для просмотра завершенной транзакции.
NB: в / account / john, "john" должен действительно быть уникальным номером счета Джона.
источник
Отличный вопрос, REST в основном объясняется примерами, подобными базам данных, где что-то хранится, обновляется, извлекается, удаляется. Подобных примеров несколько, где сервер должен каким-то образом обрабатывать данные. Я не думаю, что Рой Филдинг включил что-либо в свой тезис, который в конце концов был основан на http.
Но он говорит о «передаче состояния представительства» как о машине состояний, когда ссылки переходят в следующее состояние. Таким образом, документы (представления) отслеживают состояние клиента, а не сервер, который должен это делать. Таким образом, не существует состояния клиента, только указывается, по какой ссылке вы находитесь.
Я думал об этом, и мне кажется разумным, чтобы сервер обрабатывал что-то для вас, когда вы загружаете, сервер автоматически создает связанные ресурсы и дает вам ссылки на них (на самом деле это не не нужно автоматически создавать их: он может просто сказать вам ссылки, и он будет создавать их только тогда, когда вы последуете за ними - ленивое создание). А также, чтобы дать вам ссылки для создания новых связанных ресурсов - связанный ресурс имеет тот же URI, но длиннее (добавляет суффикс). Например:
/transaction
глюки будут вызывать создание нескольких таких ресурсов, каждый с разным URI./transaction/1234/proposed
,/transaction/1234/committed
Это похоже на то, как работают веб-страницы: на последней странице написано: «Вы уверены, что хотите это сделать?» Эта конечная веб-страница сама является представлением состояния транзакции, которое включает в себя ссылку для перехода в следующее состояние. Не только финансовые операции; также (например) предварительный просмотр, а затем зафиксировать в Википедии. Я предполагаю, что различие в REST состоит в том, что у каждой стадии в последовательности состояний есть явное имя (его URI).
В реальных транзакциях / продажах часто существуют разные физические документы для разных этапов транзакции (предложение, заказ на покупку, получение и т. Д.). Еще больше для покупки дома, с поселением и т. Д.
OTOH Это похоже на игру с семантикой для меня; Меня не устраивает номинализация преобразования глаголов в существительные, чтобы сделать его RESTful, «потому что он использует существительные (URI) вместо глаголов (вызовы RPC)». т. е. существительное «зафиксированный ресурс транзакции» вместо глагола «совершить эту транзакцию». Я предполагаю, что одним из преимуществ номинализации является то, что вы можете ссылаться на ресурс по имени, вместо того, чтобы указывать его каким-либо другим способом (например, поддерживать состояние сеанса, чтобы вы знали, что такое «эта» транзакция ...)
Но важный вопрос: каковы преимущества этого подхода? т.е. чем этот стиль REST лучше стиля RPC? Является ли методика, которая отлично подходит для веб-страниц, также полезна для обработки информации, помимо хранения / извлечения / обновления / удаления? Я думаю, что ключевым преимуществом REST является масштабируемость; один из аспектов этого заключается в том, что нет необходимости явно поддерживать состояние клиента (но делать его неявным в URI ресурса, а следующие состояния указывать в виде ссылок в его представлении). В этом смысле это помогает. Возможно, это также помогает в наслоении / конвейерной обработке? OTOH только один пользователь будет смотреть на свою конкретную транзакцию, поэтому нет никакого преимущества в том, чтобы кэшировать ее, чтобы другие могли ее прочитать - большой выигрыш для http
источник
Если вы отступите, чтобы подвести итоги обсуждения, вполне понятно, что REST не подходит для многих API, особенно когда взаимодействие клиента и сервера по своей сути является состоянием, как это происходит с нетривиальными транзакциями. Зачем прыгать через все предложенные обручи, как для клиента, так и для сервера, чтобы педантично следовать некоторому принципу, который не подходит к проблеме? Лучший принцип - дать клиенту самый простой, естественный и продуктивный способ составить заявку.
Таким образом, если вы действительно выполняете много транзакций (типов, а не экземпляров) в своем приложении, вам действительно не следует создавать RESTful API.
источник
Я отошел от этой темы на 10 лет. Возвращаясь, я не могу поверить, что религия маскируется под науку, в которую вы вступаете, когда вы гуглите отдых + надежный. Путаница мифическая.
Я бы разделил этот широкий вопрос на три части:
Это важно, потому что позволяет всем последующим запросам быть полностью идемпотентными, в том смысле, что если они повторяются n раз, они возвращают один и тот же результат и больше ничего не вызывают. Сервер хранит все ответы против идентификатора действия, и если он видит тот же запрос, он воспроизводит тот же ответ. Более полное описание шаблона в этом документе Google . Документ предлагает реализацию, которая, я считаю (!), В целом следует принципам REST. Эксперты наверняка скажут мне, как это нарушает других. Этот шаблон может быть полезен для любого небезопасного вызова вашего веб-сервиса, независимо от того, вовлечены ли в него нисходящие транзакции.
Ваше требование является фундаментальным. Не позволяйте людям говорить вам, что ваше решение не кошерное. Оцените их архитектуру в свете того, насколько хорошо и насколько просто они решают вашу проблему.
источник
Вы должны были бы свернуть свой собственный «идентификатор транзакции» тип управления TX. Так было бы 4 звонка:
Вам придется обрабатывать сохранение действий в БД (если балансировка нагрузки) или в памяти или что-то подобное, затем обрабатывать коммит, откат, тайм-аут.
Не очень хороший день в парке.
источник
Я думаю, что в этом случае вполне приемлемо нарушить чистую теорию REST в этой ситуации. В любом случае, я не думаю, что на самом деле в REST есть что-то, что говорит о том, что вы не можете касаться зависимых объектов в бизнес-кейсах, которым это требуется.
Я действительно думаю, что не стоит лишних шагов, через которые вы бы перепрыгнули, чтобы создать собственный менеджер транзакций, когда вы могли бы просто использовать базу данных для этого.
источник
Прежде всего, перевод денег - это то, что вы не можете сделать за один вызов ресурса. Действие, которое вы хотите сделать, это отправка денег. Таким образом, вы добавляете ресурс для перевода денег на счет отправителя.
Готово. Вам не нужно знать, что это транзакция, которая должна быть атомарной и т. Д. Вы просто переводите деньги иначе. отправить деньги от А до Б.
Но для редких случаев здесь общее решение:
Если вы хотите сделать что-то очень сложное, включающее много ресурсов в определенном контексте с множеством ограничений, которые фактически пересекают барьер «что против того, почему» (знание бизнеса и внедрения), вам нужно перенести состояние. Поскольку REST не должен содержать состояния, вам, как клиенту, необходимо перенести состояние в другое.
Если вы передаете состояние, вам нужно скрыть информацию внутри от клиента. Клиент не должен знать внутреннюю информацию, необходимую только для реализации, но не несет информацию, относящуюся к бизнесу. Если эта информация не имеет деловой ценности, состояние должно быть зашифровано и должна использоваться метафора, такая как токен, пароль или что-то еще.
Таким образом, можно передавать внутреннее состояние, используя шифрование и подписывание системы, и при этом быть в целости и сохранности. Поиск правильной абстракции для клиента, почему он передает информацию о состоянии, зависит от дизайна и архитектуры.
Реальное решение:
Помните, что REST говорит о HTTP, а HTTP идет с концепцией использования куки. Эти куки часто забываются, когда люди говорят о REST API, рабочих процессах и взаимодействиях, охватывающих несколько ресурсов или запросов.
Помните, что написано в Википедии о HTTP-куки:
В общем, если вам нужно передать состояние, используйте куки. Он разработан по той же самой причине, это HTTP, и поэтому он совместим с REST по дизайну :).
Лучшее решение:
Если вы говорите о клиенте, выполняющем рабочий процесс с несколькими запросами, вы обычно говорите о протоколе. Каждая форма протокола поставляется с набором предварительных условий для каждого потенциального шага, например, выполнить шаг A, прежде чем вы сможете выполнить B.
Это естественно, но раскрытие протокола клиентам усложняет все. Чтобы избежать этого, просто подумайте, что мы делаем, когда нам приходится делать сложные взаимодействия и вещи в реальном мире ... Мы используем агента.
Используя метафору Агента, вы можете предоставить ресурс, который может выполнить все необходимые для вас действия, и сохранить фактическое назначение / инструкции, на которые он действует, в своем списке (чтобы мы могли использовать POST для агента или «агентства»).
Сложный пример:
Покупка дома:
Вам необходимо доказать свою правдоподобность (например, предоставить записи в суде), вам необходимо предоставить финансовые реквизиты, вам нужно купить реальный дом у адвоката и доверенной третьей стороны, хранящей средства, убедиться, что дом теперь принадлежит вам, и добавьте информацию о покупках в свои налоговые отчеты и т. д. (например, некоторые шаги могут быть неправильными или что-то в этом роде).
Эти шаги могут занять несколько дней, некоторые могут быть выполнены параллельно и т. Д.
Для этого вы просто даете агенту задание купить дом вроде:
Готово. Агентство отправляет вам обратно ссылку, которую вы можете использовать для просмотра и отслеживания статуса этой работы, а все остальное выполняется агентами агентства автоматически.
Подумайте о трекере ошибок, например. В основном вы сообщаете об ошибке и можете использовать идентификатор ошибки, чтобы проверить, что происходит. Вы даже можете использовать сервис для прослушивания изменений этого ресурса. Миссия выполнена.
источник
Вы не должны использовать транзакции на стороне сервера в REST.
Один из недостатков ОТДЫХА:
Единственный способ RESTful - создать журнал повторных транзакций и перевести его в состояние клиента. С запросами клиент отправляет журнал повторов, а сервер повторяет транзакцию и
Но, возможно, проще использовать технологию, основанную на сеансах сервера, которая поддерживает транзакции на стороне сервера.
источник
Я полагаю, что это был бы случай использования уникального идентификатора, сгенерированного на клиенте, чтобы гарантировать, что сбой соединения не подразумевает двуличность, сохраненную API.
Я думаю, что использование сгенерированного клиентом поля GUID вместе с объектом переноса и обеспечение того, чтобы тот же GUID не был повторно вставлен снова, было бы более простым решением вопроса банковского перевода.
Не знаю о более сложных сценариях, таких как бронирование нескольких авиабилетов или микро-архитектуры.
Я нашел статью на эту тему, касающуюся опыта работы с атомарностью транзакций в сервисах RESTful .
источник
В простом случае (без распределенных ресурсов) вы могли бы рассматривать транзакцию как ресурс, где процесс ее создания достигает конечной цели.
Таким образом, для перехода между
<url-base>/account/a
и<url-base>/account/b
вы можете опубликовать следующее<url-base>/transfer
.Это создаст новый ресурс переноса и вернет новый URL переноса, например
<url-base>/transfer/256
.В момент успешной публикации на сервере выполняется «реальная» транзакция, а сумма удаляется из одной учетной записи и добавляется в другую.
Это, однако, не распространяется на распределенную транзакцию (если, скажем, «a» хранится в одном банке за одной услугой, а «b» - в другом банке за другой услугой), за исключением того, чтобы сказать «попытаться сформулировать все операции способами, которые не требуют распределенных транзакций ".
источник
Я думаю, вы могли бы включить TAN в URL / ресурс:
Просто идея.
источник