2013-11-13 1 views
21

Я читал article на HATEOAS, и хотя я понимаю идею предоставления URL-адресов для дальнейших действий в ответе, я не вижу, где вы указываете, какие HTTP-глаголы должны использоваться для взаимодействия с этими URL-адресами.Где в архитектуре HATEOAS вы указываете HTTP-глаголы?

Например, из What is HATEOAS and why is it important for my REST API?, как из этого ответа

GET /account/12345 HTTP/1.1 

HTTP/1.1 200 OK 
<?xml version="1.0"?> 
<account> 
    <account_number>12345</account_number> 
    <balance currency="usd">100.00</balance> 
    <link rel="deposit" href="/account/12345/deposit" /> 
    <link rel="withdraw" href="/account/12345/withdraw" /> 
    <link rel="transfer" href="/account/12345/transfer" /> 
    <link rel="close" href="/account/12345/close" /> 
</account 

вы знаете, если я должен выдать HTTP PUT или POST к /account/12345/close?

+0

Вы не должны использовать свои собственные глаголы. Вы должны выпустить 'DELETE' на'/account/12345', 'PUT'over'/account/12345' с новым итогом, [или лучше 'POST' для'/account/12345/operations' с правильным телом , – moonwave99

+0

@ moonwave99 - но как вы на самом деле знаете? Вы просто делаете предположение, которое может соответствовать или не соответствовать тому, что построил API-интерфейс. – user2088756

ответ

5

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

Если мы возьмем Роя Филдинга за его слово, невозможно написать большинство коммерческих интерактивных клиентских приложений как SOA RESTful/HATEOAS, используя HTTP/HTML. Возможно, это возможно в других средах, я не могу сказать.

Так практический ответа «посмотреть его в документации» и «написать клиент с этим приложением-знанием в я т» с боковой порцией "игнорировать тот факт, что мы «нарушая правила Филдинга, делая это».

Я склонен создавать JSON ответы, которые обеспечивают этот подход:

GET /account/12345 HTTP/1.1 
{ 
    "account": { 
     "number": "12345", 
     "currency": "usd", 
     "balance": "100.00", 
     "deposit": { 
      "href": "/account/12345/deposit", 
      "action": "POST" 
     }, 
     "withdraw": { 
      "href": "/account/12345/withdraw", 
      "action": "POST" 
     }, 
     "transfer": { 
      "href": "/account/12345/transfer", 
      "action": "POST" 
     }, 
     "close": { 
      "href": "/account/12345/close", 
      "action": "DELETE" 
     } 
    } 
} 

... добавить дополнительные свойства конструкции по мере необходимости, но эти основы.

Я считаю, что это позволяет потребителю (клиентам) потребления писать RESTful способом, но при этом я использую Тело ответа, которое, по словам Филдинга, не то, что он намеревался.

Я бы предложил такое объяснение индивидуальный к ответу, хотя:


Филдинг говорит: «Я получаю разочарование по количеству людей, призывающих любой HTTP-интерфейс на основе REST API.» (http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven).

Обратите внимание, как он так остро говорит «any« HTTP-интерфейс.

Самый актуальный сегмент в его «лекции» заключается в следующем:

"Отдыхают API должны быть введены без предварительного знания за пределами исходного URI (закладки) и набор стандартных типов носителей, которые подходят для предполагаемой аудитории (т. е. ожидается, что ее понимает любой клиент, который может использовать API). С этого момента все переходы состояния приложения должны управляться выбором клиента по предоставленным сервером вариантам, которые присутствуют в полученных представлениях или подразумеваются путем манипулирования пользователем этими представлениями. Переходы могут быть определены (или ограничены) знаниями клиента о типах медиа и механизмах обмена ресурсами, как из h может быть улучшено «на лету» (например, код по требованию). [Failure здесь означает, что вне полосы информация является движущей силой взаимодействия вместо гипертекста.] "

Он говорит, что это потому, что HTTP/HTML приложение URI медиа-типа просто„текст/html“, и где это глагол в этом? Нет никого.URI не может рассказать вам, что для чего-то требует Verb для использования/навигации, вы не можете использовать только данные внутри диапазона, чтобы динамически создавать «следующую» навигацию в своем клиенте.

Он пояснил, что считает, что мы предоставляем наши URI как часть CDATA, которая включает в себя «метод» или что контекст URI сам по себе обеспечит его, как это делает элемент FORM. Он явно решает против API OpenSocialst REST, заявляя, что он не RESTful.

Здесь: «элементы привязки с атрибутом href создают гипертекстовую ссылку, которая при выборе вызывает запрос на поиск (GET) в URI, соответствующий атрибуту href, кодированному CDATA». Идентификаторы, методы и типы файлов являются ортогональными проблемами - методы не задаются типом медиа. Вместо этого тип носителя сообщает клиенту, какой метод использовать (например, якорь подразумевает GET) или как определить используемый метод (например, элемент формы говорит, чтобы искать в атрибуте метода). Клиент должен уже знать, что означают методы (они универсальны) и как разыгрывать URI.

Обратите внимание, что он говорит, что клиент уже должен знать, что методы означают, он не говорит о том, что клиент уже должен знать, что они являются - вот почему вы спросили ваш вопрос. Многие люди борются с этим, потому что мы фактически не создаем наши приложения, как это, в большинстве сред SOA.

Как и многие инженеры, я просто хочу, чтобы Филдинг вышла с разъяснением или повторным заявлением, но не только он этого не сделал, он опубликовал еще два указаний на нас, как инженеров, удвоив его заявив, что мы должны перестать называть RESTful нашего API и признать, что мы создаем RPC.

Я думаю, что подобный JSON подход является мостом reasoble, но у меня нет ответа на то, что мы используем тело запроса для этого, а не полагаемся на Media Type, чтобы это подразумевалось.

И, наконец, есть новый глагол в HTTP, называемый OPTIONS, который для данного URI будет вернуть допустимый список действий Verb. Я думаю, что у Филдинга была написана эта HTTP-версия. Это позволило бы Клиенту в целом создавать URI-навигацию без запрещенного внутреннего знания приложений. Но есть три проблемы, которые я могу думать в реальном мире:

  1. вы бы кодировать механизм в вашей службе Aggregation, чтобы сделать этот вызов для каждого URI вы пытаетесь вернуться, и так много данных содержит много адресов URI (_links в HAL), который добавляет много дополнительных «перелетов» в вашу конструкцию Response Response. Вероятно, мы все будем жаловаться на это.
  2. Практически ни один SOA-сайт, претендующий на роль RESTful, фактически реализует вызов-метод-метод OPTIONS, чтобы вы могли выполнить этот запрос в любом случае.
  3. Мы все будем жаловаться на «ненужные» дополнительные звонки, которые он добавляет (особенно в мире электронной коммерции), на обработку Клиента и его тенденцию подталкивать нас к требованиям SLA.

+0

Я верю в разговоры об «RIS Misconceptions» (https://www.infoq.com/presentations/rest-misconceptions), он означает RDF и Dublin Core. Так что, возможно, динамика концепции должна зависеть от словаря? Поэтому, если у вас есть банковский счет без овердрафта, у вас есть положения, а один с вами либо должен иметь отдельные условия, либо испечь требование в словаре, что им нужно проверить политику овердрафта. – ArtB

26

Не помещайте глаголы в URI (например,/account/12345/transfer). URI представляют ресурсы, а не действия.

глаголов для использования определяется протоколом HTTP (например GET, POST, PUT, OPTIONS, DELETE и т.д.). REST - это архитектура с набором ограничений, а HTTP - это протокол, который придерживается этих ограничений. HTTP определяет ограниченный набор глаголов для передачи состояния ресурса с клиента на сервер и наоборот. По определению вы ограничены только этими глаголами.

Клиент должен решить, какой HTTP-глагол использовать на основе того, что он пытается сделать. Серверу не нужно сообщать ему, какие существуют глаголы, он уже знает на основе протокола HTTP.

Если клиент должен знать, какие глаголы он может использовать на ресурсе, он может запросить ресурс с помощью глагола OPTIONS и посмотреть заголовок Allow в ответе (при условии, что сервер вернет эту информацию, которая должна быть, если она полезно). Некоторые ресурсы могут принимать только GET, в то время как другие могут принимать других, таких как POST и PUT.

Просмотрите спецификацию HTTP, чтобы узнать, какой глагол использовать в каком контексте.

Чтобы привести пример из вашего исходного сообщения. Скажем, у вас есть ресурс учетной записи с URI по адресу

/accounts/12345 

и вы хотите закрыть учетную запись. Помните, что REST - государственный перевод. Клиент закрывает учетную запись, поэтому у нее есть учетная запись в состоянии закрытой на ее конце. Затем он передает это состояние серверу, чтобы клиент и сервер совпадали друг с другом. Таким образом, вы PUT состояния клиентов (который является ресурсом в закрытом состоянии) на сервер

PUT /accounts/12345 

Тело запроса должно содержать представление ресурса в закрытом состоянии. Предполагая, что вы используете XML для представления ресурса учетной записи, было бы что-то вроде этого

PUT /accounts/12345 

<?xml version="1.0"?> 
<account> 
    <account_number>12345</account_number> 
    <balance currency="usd">100.00</balance> 
    <state>closed</state> 
</account> 

Ресурс на сервере в настоящее время зеркала ресурса на клиенте. Оба находятся в закрытом состоянии. Если вы не хотите передавать весь ресурс каждый раз, когда вы вносите изменения в один из его атрибутов, вы можете разделить их на иерархию ресурсов. Сделайте состоянии счета свой ресурс и PUT к тому, чтобы изменить его

PUT /accounts/12345/status 

<?xml version="1.0"?> 
<state>closed</state> 
+2

Существует хорошая статья [здесь] (https: // opencredo.com/design-rest-api-fine-grained-resources-hateoas-hal /) за то, почему вы принимаете действия, моделируемые как ресурсы. – bblue

+0

На самом деле я не понимаю, что они делают в этой статье. Они говорят: «Если вы обратитесь к диаграмме состояний, вы увидите, что все различные возможные переходы вообще не передаются в контракте API». Это хорошо, что различные состояния, в которых может быть ресурс, не должны быть в схеме URL. Они, похоже, делают это, чтобы не документировать состояния для клиента. Для этого используются типы контента. И клиент, и сервер должны знать о действительных состояниях, в которых может находиться ресурс. –

+0

Прочтите статью еще раз и, на мой взгляд, они в корне ошибочно считают REST. Кажется, они думают, что вам нужно предоставить ресурсы в качестве триггеров для перемещения состояния на сервере. Таким образом, чтобы переместить ресурс 'audit' в состояние' принятого', вы создаете новый ресурс в 'audits/1/accept'. Нет, вы просто положили 'audit/1' в состояние приема ** на клиенте ** и' PUT' обратно на сервер. Вы не должны (и не должны) конструировать конечные точки URL для запуска различных переходов состояний. –

7

вы знаете, если вы должны PUT или POST к/счета/12345/закрыть?

Проконсультируйтесь с документацией по API, вот как вы знаете. HATEOS не является заменой официальной документации. Документация необходима для API REST, как и любой другой API.

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

Если вы хотите узнать, как использовать REST API, прочитайте документацию. Если вы хотите, чтобы кто-то другой использовал ваш REST API, предоставить им документацию.

Здесь нет никакой магии.

ответ
+1

Ах, я думал, что это должно быть все самодостаточным. Я получу смелость, чтобы они отдали часть вашего ответа. – ArtB

+0

Я должен содержаться в протоколе HTTP и документации для используемого вами типа контента. Он не должен быть жестко закодирован в URI. Например, для написания веб-браузера все, что мне нужно знать, это протокол HTTP 1.1 и HTML. Мне не нужно знать конкретный план сайта www.nytimes.com, я могу понять это из протокола HTTP и HTML-документы, которые они предоставляют. Если вам нужен определенный тип контента, вы можете определить свой собственный, используя vnd.yourcompany.yourtype Content-Type, который затем можно документировать для клиентов. –

+0

Я имею в виду автономность в том смысле, что ответ дал достаточно информации о том, как использовать API. Например, если вы можете просто «УДАЛИТЬ» учетную запись, когда баланс равен _exactly_ '0', у вас нет способа указать, что в ответе, потому что вы _not_ 0, существуют другие глаголы, которые вы можете выполнить на этом URL-адресе, поэтому он попадает в список только так же, независимо от того, разрешено ли 'DELETE'. – ArtB

4

@Cormac Малхолл очень хорош, но я хотел бы предложить уточнение, что я услышал от коллеги:

действия или события, которые происходят на ресурс может рассматриваться как подчиненные существительных домена, используя gerund формы глагола действия или имени события, но должны быть помещены под значащим идентификатором пути, таким как «действия» или «события» или что-то подобное. Представление ресурса, которое будет возвращено, выражает данные состояния о действии, так что POST или PUT действует как запрос.

Предположим, что заказы имеют несколько состояний жизненного цикла. В какой-то момент после составления проекта заказ помещается, выполняется или отменяется.

Информация об этих действиях заказа будет размещена путем размещения имени действия в форме множественного существительного под пулом ресурсов с помощью /actions, чтобы вернуть данные, если состояние действий активно, или 404 NOT FOUND в противном случае.

https://order.api.foobar.com/v1.0/orders/{orderId}/actions/placements 
https://order.api.foobar.com/v1.0/orders/{orderId}/actions/fulfillments 
https://order.api.foobar.com/v1.0/orders/{orderId}/actions/cancellations 

Когда эти действия являются идемпотент (порядок не может быть помещен в два раза подряда), так что эти действия могут быть запрошены PUT «Инжем соответствующего представления этой URI. Когда они не являются идемпотентными, они создаются в форме множественного числа POST.

Например, чтобы отслеживать порядок разрешительных, мы могли бы POST к:

https://order.api.foobar.com/v1.0/orders/{orderId}/approvals 

, а затем мы видим информацию об отдельных утверждениях, делая GET против:

https://order.api.foobar.com/v1.0/orders/{orderId}/approval/1 

Это часто бывает полезно используйте совокупность, называемую «действия», чтобы найти все действия:

https://order.api.foobar.com/v1.0/orders/{orderId}/actions 

Мы могли бы POST, чтобы представление объявляло, что означает действие.

Вы также можете получить список действий, через индивидуальные заказы, оставляя параметр {orderId} от:

https://order.api.foobar.com/v1.0/orders/actions/placements 
https://order.api.foobar.com/v1.0/orders/actions 

Они могут быть найдены путем добавления параметров запроса:

https://order.api.foobar.com/v1.0/orders/actions/placements?since={sinceTimestamp} 
+0

Хотя это может сработать, оно работает против преимуществ архитектуры RESTful, поскольку оно слишком тесно связано с схемой URL-адресов с состояниями, в которых может находиться ресурс. Определение правильных состояний должно обсуждаться через Content Type представления ресурса, а не схему URL. Это может включать определение типа «заказов» типа содержимого, о котором знают клиент и сервер, но это вне схемы URL-адреса области. Немые типы контента (например, только «json») приводят к тому, что разработчики слишком много отображают логику представления в URL-адресе. Вместо этого поместите его в тип содержимого. –

+0

@CormacMulhall Мне сложно понять, как пользовательский тип мультимедиа может передать значение действий, можете ли вы утверждать, как избежать ошибки CRUD, используя этот подход? –

+0

Таким образом, тип носителя не сообщает клиенту, какие действия он может выполнять на сервере. Вместо этого это контракт между клиентом и сервером, что представления ресурсов, разделяемых между ними, будут в этих форматах. Клиент не спрашивает у сервера, какие действия я могу выполнить на вас, клиент вместо этого обновляет ресурс, следуя спецификации типа контента, до действительного нового состояния, а затем просто PUT, возвращающиеся на сервер. Поскольку они оба работают с одним типом контента, сервер также должен понимать это новое состояние. –

Смежные вопросы