我正在article上阅读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
您知道我应该向PUT
发送HTTP POST
还是/account/12345/close
?
答案 0 :(得分:30)
不要在您的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>
答案 1 :(得分:9)
你的问题在Stackoverlow上有很多答案,但大部分都是你要问的原因,我怀疑你总觉得它们部分不满意。
如果我们接受Roy Fielding的话,就不可能使用HTTP / HTML将大多数商业交互式客户端应用程序编写为SOA RESTful / HATEOAS。在其他媒介中可能有可能,我不能说。
所以实用答案是&#34; 在文档中查找&#34;和&#34; 在i t&#34;中使用该应用程序知识编写您的客户通过这种方式帮助我们忽略了我们通过这样做打破菲尔丁规则的事实&#34;
我倾向于设计提供此方法的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方式编写消费客户端,但这样做我正在使用响应主体,Fielding说这不是他想要的。
我会在答案中提供单独的解释:
Fielding说&#34;我对使用REST API调用任何基于HTTP的界面的人数感到沮丧。&#34; (http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven)。
请注意他是如何如此大力地说&#34; 任何&#34;基于HTTP的界面。
他演讲中最相关的部分&#39;是这样的:
&#34; 应输入REST API,除了适当的初始URI(书签)和标准化媒体类型 之外,不需要任何其他知识对于目标受众(即,任何可能使用API的客户都应该理解)。从那时起,所有应用程序状态转换必须由客户端选择服务器提供的选择来驱动,这些选择存在于接收的表示中或者由用户对这些表示的操纵所暗示。转换可以由客户端对媒体类型和资源通信机制的知识来确定(或限制),这两者都可以在运行中(例如,按需代码)进行改进。 [失败在这里意味着带外信息正在推动交互而不是超文本。] &#34;
他说这是因为HTTP / HTML app URI media-type 只是&#34; text / html&#34;,其中的动词在哪里?没有一个。 URI无法告诉您Verb需要使用/导航的内容,因此您不能仅仅使用带内数据来构建“下一步”。在您的客户端中动态导航。
他解释说他相信我们将我们的URI作为CDATA的一部分,包括&#34;方法&#34;或者URI的上下文无疑会提供它,就像FORM元素那样。他明确指责OpenSocialst REST API声明它不是RESTful。
此处:“具有href属性的锚元素创建一个超文本链接,选中此链接后,将对与CDATA编码的href属性对应的URI调用检索请求(GET)。”标识符,方法和媒体类型是正交关注点 - 方法没有给出媒体类型的含义。相反,媒体类型告诉客户端使用什么方法(例如,锚意味着GET)或如何确定要使用的方法(例如,表单元素表示查看方法属性)。客户端应该已经知道方法的含义(它们是通用的)以及如何取消引用URI 。
请注意,他说客户应该已经知道意味着的方法,他并没有说客户应该已经知道他们是什么 - 这就是你问的原因你的问题。很多人都在努力解决这个问题,因为事实上我们并没有在大多数SOA环境中构建这样的应用程序。
像许多工程师一样,我只是希望菲尔丁能够做出澄清或重新声明,但他不仅没有这样做,他还作为工程师向我们发出了两个进一步的警告,他的声明加倍了,说我们应该停止调用我们的API RESTful并接受我们正在构建RPC。
我认为类似JSON元素的方法是一个可以理解的桥梁,但我无法回答我们使用请求主体这样做的事实,而不依赖于媒体类型来暗示它。
最后,在HTTP中有一个名为OPTIONS的新动词,对于给定的URI,将返回允许的动词动作列表。我认为Fielding有一手编写这个HTTP版本。这将允许客户端在没有禁止的内部应用程序知识的情况下一般地构造URI导航。但这有三个我在实际世界中可以想到的问题:
答案 2 :(得分:7)
你知道你应该PUT或POST到/ account / 12345 / close吗?
您可以参考API的文档,这就是您所知道的。 HATEOS不是正式文档的替代品。 REST API需要文档,就像任何其他API一样。
HATEOS可让您了解特定资源的其他选项。它没有告诉您为什么要使用这些选项,或者您将发送哪些信息。内容类型仅表达语法和高级语义,而不表示应用程序级语义,因此它们也不是文档。
如果您想知道如何使用REST API,请阅读文档。如果您希望其他人使用您的REST API,请向他们提供文档。
这里没有魔力。
答案 3 :(得分:4)
@Cormac Mulhall的答案非常好,但我想建议我从同事那里得到的改进:
可以使用动作动词的动名形式或事件名称将发生在资源上的动作或事件视为从属域名词,但应将其置于有意义的路径标识符下,例如“actions”或“events”或类似的东西。将返回的资源表示形式表示有关操作的状态数据,以便POST
或PUT
作为请求进行操作。
假设订单有多个生命周期状态。在起草后的某个时刻,下订单,履行或取消订单。
有关这些订单操作的信息将通过将操作名称放在具有/actions
的资源路径下的复数名词形式来定位,以在操作状态为活动时返回详细信息,否则返回404 NOT FOUND
。 p>
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
使用名为“actions”之类的聚合来查找所有操作通常很有用:
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}