RESTful API设计和CQRS

6

我在思考如何使RESTFul API更加意图清晰。在各种博客中,我看到的一个常见模式是传统REST API会导致:

封禁玩家 -> POST /players

但如果我改用更有意图表现力的接口,则可以使用:

封禁玩家 -> POST /players/{ playerid }/banPlayer

我感觉第二个更能表现意图。

团队常常反对第二个接口不符合REST风格。

同时,目前我无法放弃RESTful API。

我想听听你的想法。


2
REST并不关心URI的设计,因为它只是一种将客户端与服务器解耦的技术,客户端不应尝试从URI中提取知识,因为如果服务器在某个时候更改URI,则可能会破坏。相反,客户端和API都应该使用关系名称,并支持表达特定语义的特殊媒体类型。媒体类型只是一个语法及其数据交换语义的纯文本描述。 - Roman Vottner
那么,(略带玩笑的)BAN /players/{playerid} HTTP/3 怎么样? - Darragh
除了标签和标题之外,我认为你忘记提到CQRS在这方面的作用了。 - istepaniuk
6个回答

10

在Restful API设计中,对于如何将操作(actions)应用于资源,有两种不同的观点。

  1. 您可以在URI中描述要执行的资源操作:

    请求URI:
    POST /players/{id}/ban

    注意:只需使用ban即可 - 我们已知资源是玩家,它在基本URI中。

  2. 您可以在请求正文中包含操作:

    请求URI:
    POST /players/{id}

    请求正文:
    { 'action': 'ban' }

您可以选择其中任意一种方式 - 无论您偏好哪种,都有很多讨论,但最终两种都是正确的。

注意:

我在这里的假设是禁止玩家不仅仅是更新其部分信息,而是涉及到与玩家相关的系统操作(或状态转换)。否则,如果它只是对玩家资源进行更新,则应根据情况使用PATCH或PUT进行处理。

以下是一些参考讨论:

如果您进行一些谷歌搜索,还有更多的讨论...


3
考虑使用 PATCH 替代 POST,我会点赞。 - Quality Catalyst
2
@QualityCatalyst 我的假设是禁止玩家不仅仅是更新其某个部分,而是涉及到玩家的系统操作。否则我会同意你并且你也会完全摆脱“ban”Uri部分。 - shenku
参考讨论包括:http://restful-api-design.readthedocs.io/en/latest/methods.html,https://github.com/interagent/http-api-design/issues/58,https://nordicapis.com/designing-a-true-rest-state-machine/,https://softwareengineering.stackexchange.com/questions/141410/restful-state-changing-actions - shenku
另一种方法是考虑“禁止”资源。因此,第一种方法是正确的。另一方面,第二种方法看起来像是远程过程调用(RPC)风格,这与Restful哲学相悖。 - caeus
1
我同意@Quality Catalyst的观点。这应该是一个PATCH请求,并且只应使用第二种方法。PATCH文档应包含在请求正文中。PATCH支持更改状态的操作(动作)。您的操作是“禁止”。这与REST一致。 - Yuli Bonner
显示剩余2条评论

2
长话短说:强制性地进行意图披露并不应该是必须的,但如果你想在API上添加一些关于它的外观DDD,那么没有什么可以阻止你这样做。
根据HATEOAS约束条件的RESTful Web API(此约束条件是REST“统一接口”特性的重要组成部分,如Roy Fielding博士论文中所定义的),API的软件客户端不应关心URL。每个可能和允许的操作都应包含在响应中,带有相应的链接关系和URI。这样,您只需要硬编码链接关系即可。
这个限制并不妨碍您让API更加意图明确,以便于人类客户端理解整体架构。我建议您选择这条路,因为人类用户和他们编写的软件一样重要。
Roy Fielding在他的博客文章中也提到了这一点。

2

既然你要求的是RESTful而不是最佳方式,这是我的想法。

以下是RESTful的URI选项:

  • /players
  • /players/{ playerid }/banPlayer
  • /player-banning
  • /entities?action=ban_player&method=PUT
  • /banana
  • 其他任何内容,REST不规定URI应该是什么样子的

RESTful的方法是纯粹通过超文本来公开下一个可用状态的知识。为了实现REST,您必须使用超文本作为应用程序状态的引擎(HATEOAS)。依赖客户端对URI的了解是依赖于带外知识,这与REST是相反的。

您的资源不需要直接映射到业务对象。如果选择,您可以将用户意图本身表示为资源,例如禁止玩家事件资源。您可以向其POST一些有关要禁止的玩家的信息,随后的GET将提供有关事件的信息。

哦,只是因为REST不关心URI是什么,并不意味着您不应该关心。您只需使用不同的标准来决定什么是最好的。


1
根据REST API的方法,你需要在URI中使用你的实体,因此,由于banPlayer不是一个实体,所以你不能使用它。我建议使用PUT方法更新你的记录。这里你可以了解更多规则。实际上,关于URIs的第一部分正好适用于你的情况。

1
常见反对意见是团队认为第二个不符合REST风格。简单的回答是:无论是否符合REST,API的一致性都具有价值。因此,“这不是我们在这里所做的方式”将胜过“但是REST说”。在API中拼写URI与在代码中拼写方法名称非常相似。有许多不同的样式参数,但“本地约定”本身就是一个很强的参数。尽管如此,REST并不关心您为标识符使用什么拼写方式。这就是Fielding在2008年所说的话。 REST Fielding had to say in 2008 一个REST API应该在定义用于表示资源和驱动应用程序状态的媒体类型或在为现有标准媒体类型定义扩展关系名称和/或超文本启用的标记方面花费几乎所有的描述性工作。 在感兴趣的URI上使用什么方法的任何努力都应完全在媒体类型的处理规则范围内定义(在大多数情况下,已由现有媒体类型定义)。[这里的失败意味着带外信息正在驱动交互,而不是超文本。]
带内将URI包含在资源的表示中-将其放入HTML文档中表单的描述中。 带外是记录URI,并期望人们正确处理它。
注意:人类可读的URI或记录应使用的URI没有问题。 但请注意,即使编写您的浏览器的人没有阅读 stack overflow's API documentation,您也可以向stackoverflow发布问题-这就是REST。

0

这篇Google Cloud文章API设计:理解gRPC、OpenAPI和REST以及何时使用它们澄清了REST与RPC之争。REST更适用于面向实体的API,而RPC更适用于面向操作的API(以及CQRS)。具有超媒体控制的最成熟REST级别3仅适用于具有简单状态模型的实体。

首先了解和评估REST对您的情况的好处。许多API是类REST而不是RESTful。OpenAPI实际上是映射到HTTP端点的RPC,但这并不妨碍它被广泛采用。


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