我有一个可以在 URI /resources/{resource_identifier}
访问的资源,它有一个我希望可以访问的"状态"属性。我想到了几个选项,哪个是"最好"或"最完整的"?
选项 1 将操作追加到 URI 上,并让客户端POST
到这些 URI
/resources/{resource_identifier}/void
/resources/{resource_identifier}/open
/resources/{resource_identifier}/close
不过这看起来很笨拙。
选项 2 在 URI 中使用查询参数,并使客户端PATCH
这些
/resources/{resource_identifier}?transition=void
/resources/{resource_identifier}?transition=open
/resources/{resource_identifier}?transition=close
选项 3 使用请求的有效负载并让客户端PUT
/resources/{resource_identifier}
有效负载选项:
{ ..., "status" :"void" }
{ ..., "status" :"open" }
{ ..., "status" :"close" }
或者也许完全是别的什么?
第一个选项显然不是 REST; 您在 URI 中有"操作"并且正在使用 POST
,即创建一个新资源,您显然没有尝试这样做。
现在只看 URI 格式。选项二越来越好,但这种性质的查询字符串更多地用于读取数据。没有什么能真正阻止你以这种方式这样做。选项 3 具有最佳 URI 格式,它仅引用要在请求中引用的资源。
如果我们现在考虑请求的方法。在我的书中,这相当简单,我假设状态只是该资源的一个字段,因此如果您只进行部分更新,则正在修补资源,因此PATCH
是要使用的方法。如果"状态"是唯一的属性,那么更改状态就是完全更改资源,因此PUT
是可以接受的;但我怀疑事实是否真的如此。
就目前而言,第三个选项的 URI 与PATCH
的使用相结合可能是最佳选择。
PATCH /resources/{resource_identifier}
{ "status" :"close" }
当然,您也可以将其与通过其自己的 URI 公开特定属性的概念相结合,就好像它们本身就是资源一样。坦率地说,我不喜欢这样,因为它感觉很奇怪,一次只适用于一个属性。不过,如果这是您想要使用的,您可以拥有以下内容:
PUT /resources/{resource_identifier}/status
close
请记住,没有"正确"的REST方法,只有"不错"的方法。这是一种风格,而不是规则集。
我还建议您考虑,一般来说,能够采用多种格式是一个理想的功能。因此,第一个选项往往更容易使用。您可以像示例一样采用 JSON,或者将其交换为 XML <status>close</ status>
,或者一个简单的键值对status=closed
等。
为什么不将"状态"作为资源。您可以管理它。还假设应该已经创建了一个作为{resource_identifier}
资源创建的一部分的"状态",并且已经有一个状态的默认值。
然后,业务逻辑需求只是通过 rest 调用"更新"状态,因此应该使用"PUT"。
更新将状态移动到放置主体
PUT: /resources/{resource_identifier}/status/
Body: {void | open | close }
在许多情况下,状态更改具有许多业务逻辑。虽然答案是基于"Rest 标准",但我认为有些情况不仅仅是更改状态字段。
例如,如果系统必须取消订单。这不仅仅是更改状态,因为订单具有许多状态,并且每个更改都代表许多逻辑(通知,验证等(。
在使用的情况下:
PATCH /order/1
{ "status" :"cancelled" }
我可以向你保证,逻辑会被客户端和服务器分散,很难维护,也不会有优雅的代码(尤其是在服务器端,它会验证之前的、后续的状态,如果更改一致,它会与更新责任混合在一起(。
我认为使用取消方法并完成其工作更简单。我认为在很多情况下这样做更优雅:
PATCH: /order/1/cancel
//you could use the body with some cancellation data.
以下链接可以帮助您: https://phauer.com/2015/restful-api-design-best-practices/#keep-business-logic-on-the-server-side
您的第二个选项看起来更好,因为您正在维护 RESTful url 结构,而不是将 RPC 样式的方法附加到其末尾。
为什么不这样做:
PUT
/resources/:id
数据并随请求一起发送transition=void
。
它的行为方式与您收到 POST 请求时的行为方式相同,只需从请求正文中获取数据即可。
由于 PUT、GET 和 DELETE 的标准行为大致映射到 CRUD 范式,因此有些人认为资源 API 应该只用于 CRUD 用例。但是,这是一个不正确的评估,因为 POST 可用于执行不能很好地映射到 CRUD 的行为。
在我自己看来,我使用这里普遍同意的两种结构。但是,以下是我如何划清界限。
在对资源属性的更新有副作用的情况下(例如,如果属性的值为 XYZ,则发送邮件(,我让它拥有自己的资源(即端点。 PUT /resources/:id/status
(,这将简化控制器级别的事情。否则,我将使用其父级的资源(PATCH /resources/:id/status
(