REST/HATEOAS - HAL链接中可用的方法

13

我希望使用HATEOAS来定义REST API。具体来说,我认为指示给定资源当前可用的操作非常有趣。

由于一些HATEOAS规范对我的需求过于繁琐,所以我在查看HAL规范,因为我发现它非常简洁实用:

{
    _links: {
        self: { href: "/orders/523" },
        warehouse: { href: "/warehouse/56" },
        invoice: { href: "/invoices/873" }
    },
    currency: "USD",
    status: "shipped",
    total: 10.20
 }

然而,在HAL中,链接仅包含相关资源的列表,但不包括可用的操作。根据上面的例子,我现在是否可以取消订单,还是不能取消了?一些HAL示例通过使用特定的取消URL并且仅在可以取消时在响应中添加相应的链接来解决这个问题:

"cancel": { "href": "/orders/523/cancel" }

但这并不是很符合REST。取消不是资源。取消是一个资源的DELETE操作,即:

DELETE /orders/523

是否有一种优雅的方式可以使用HAL来表示这个问题,还是我应该使用其他的HATEOAS规范?

我正在考虑返回一个“取消”链接,其URL与self相同,但在这种情况下,客户端必须知道要使用DELETE动词来取消,这并没有在HATEOAS响应中得到描述。

self: { "href": "/orders/523" },
cancel: { "href": "/orders/523" }

这是否是符合HATEOAS/HAL建议的方法?我了解HAL没有任何“method”参数,而自己添加该参数将违反HAL规范。

2个回答

9
一些HAL示例通过使用特定的URL进行取消操作,并仅在取消操作可能时在响应中添加相应的链接来解决此问题。
是的,就像网站一样:如果您想向客户端提醒可能达到某个其他应用程序状态,则提供包括所涉及资源的标识符的链接。
但这并不太符合RESTful的设计原则。
取消是资源的删除,即:DELETE /orders/523。
您正在混淆领域模型上的操作和集成模型上的操作。REST API所做的是引导客户端通过协议实现某些目标;它不是将领域语义映射到HTTP上。
Jim Webber这样表述:

Web不是你的领域;它是一个文档管理系统。所有HTTP动词都适用于文档管理领域。URI不映射到域对象——这违反了封装性。工作(例如向域模型发出命令)是管理资源的副作用。

REST的一个约束条件是统一接口;在HTTP的情况下,这意味着所有资源以统一方式理解方法;DELETE 表示 RFC 7231,第4.3.5节中描述的语义。

换句话说,如果我发送请求

OPTIONS /x/y/z/foobar ...

如果响应中包含Allow header中的DELETE,那么我知道它的含义。你域中的副作用?我不知道任何关于副作用的事情。
在DELETE的定义中,请注意以下内容:
相对较少的资源允许使用DELETE方法 - 其主要用途是远程创作环境,在该环境中用户有一些关于其效果的指导。
无论如何,你真正询问的不是DELETE,而是HAL
从我所了解的情况来看,官方的做法是用链接关系来记录它。换句话说,不要使用“cancel”作为链接关系,而是使用类似于:

https://www.rfc-editor.org/rfc/rfc5023#section-5.4.2

然后你的消费者,如果想要了解链接的用途,可以跟随关系来了解发生了什么。

HAL讨论:为什么没有方法?有很多好的信息。

我喜欢Mike Kelly的总结:

这个想法是可用的方法可以通过链接关系文档传达,不需要在JSON消息中。


一如既往的好答案 :) 根据链接关系名称的使用,我可能会遵循RFC 5988(Web链接)中概述的方法,并使用已注册的关系类型或扩展类型,例如Dublin Coreschema.org,这些类型需要类似于答案中给出的URI,这也是HAL+JSON所推荐的。 - Roman Vottner
很好的答案,我认为OPTIONS方法是存储Rest资源可用操作的正确位置。 - danipenaperez

1
根据LosTechies的这篇文章,在CQRS的视角下,可以使用如下URL:/orders/<id>/<command> 并使用PUT请求进行调用。因此,使用"cancel": { "href": "/orders/523/cancel" }是可以的。
然而,如果您绝对想使用DELETE,并且只使用命令链接来修改资源(即/orders/<id>/<command>),就像本文所建议的那样,为什么不能添加一个链接,例如"cancel": { "href": "/orders/523" } 并省略HTTP动词呢? 我的意思是,根据REST,只有5个主要动词(GET、POST、PUT、PATCH和DELETE)。我们无法在/<ressource>/<id>这样的URL上使用POST,GET已经被定义为“self”关系,我们上面提到的修改(PUT)将由命令链接处理(即/<ressource>/<id>/<command>),因为我们使用命令链接,所以没有必要使用PATCH。之后,唯一剩下的选项就是:DELETE。
它不完美,但它有效并且不违反任何约定。

根据REST,只有5个主要动词... REST并没有具体指定这些动词,它只是在论文中提到了一些操作,例如POST在HTTP的上下文中根本没有出现。由于HTTP方法是由IANA标准化和维护的,实际上还有一些你应该考虑支持,至少它们是合适的HTTP方法,即使不被广泛使用。 - Roman Vottner
@RomanVottner 我在谈论已被接受的REST惯例和良好实践。KevinDockx在pluralsight上做了一门很棒的课程:https://app.pluralsight.com/library/courses/asp-dot-net-core-restful-api-building/table-of-contents - Maxime Gélinas
...接受了REST的常规约定和最佳实践...由谁?典型的约定规定,像“取消”这样的动词不应该被放在URI中,因为操作应该由HTTP操作来表示,而Fielding的论文一开始就没有这样的限制。在使用REST交互模型的体系结构中,URI中的字符实际上并不重要,尽管有很多人似乎认为这是“不符合REST原则”的。这样的“约定”应该小心处理,因为它们代表了一些人的意见,但不一定是真理。 - Roman Vottner
@RomanVottner 「...由誰?常規約定表明,像cancel這樣的動詞不應該放在URI中,因為操作應該由HTTP操作來表示...」JimmyBogard在這篇文章中這麼做:https://lostechies.com/jimmybogard/2016/06/01/cqrs-and-rest-the-perfect-match/ - Maxime Gélinas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接