我想知道当你拥有一个包含子资源列表的资源时哪个是最佳实践。例如,您有资源Author,其中包含姓名,ID,生日和List书籍等信息。该书籍清单仅与作者有关。因此,您有以下情形:
解决方案1
我搜索了哪个是正确的设计,我找到了多种方法。我想知道是否有一种标准的设计方法。我认为书中的设计说有以下方法:
POST /authors/{authorId}/book/
PUT /authors/{authorId}/book/{bookId}
DELETE /authors/{authorId}/book/{bookId}
解决方案2
我的解决方案是只有一个PUT方法可以完成所有这三件事,因为书籍列表只存在于对象作者中,而您实际上是在更新作者。就像是:
PUT /authors/{authorId}/updateBookList
(并在作者对象中发送整个更新的书籍列表)
我在我的场景中发现了多个错误。例如,从客户端发送更多数据,在客户端上具有一些逻辑,对API进行更多验证,并且还依赖于客户端具有最新版本的Book List。
我的问题是:这样做是反模式的吗?
情况1.在我的情况下,我的API使用的是另一个API,而不是数据库。使用的API只有一个“updateBookList”方法,所以我猜我在API中复制这种行为更容易。它也是正确的吗?
情况2.但是,假设我的API将使用数据库,那么更适合使用SOLUTION 1吗?
此外,如果您可以提供一些文章,书籍,您可以在其中找到类似的信息。我知道这种设计不是一成不变的,但有些指导方针会有所帮助。 (示例:来自Book REST API设计规则手册 - Masse - O'Reilly)
解决方案2听起来非常像旧式RPC,其中调用一个执行某些处理的方法。这就像一个REST反模式,因为REST的重点是资源,而不是方法。您可以在资源上执行的操作由底层协议(在您的情况下为HTTP)提供,因此REST应遵循底层协议的语义(少数约束之一)。
此外,REST并不关心如何设置URI,因此实际上没有RESTful URL。对于自动化系统,遵循特定结构的URI与作为URI的随机生成的字符串具有相同的语义。虽然应用程序应该使用rel
属性给应用程序可以使用的URI某种逻辑名称,但是我们这些人才能理解字符串。期望URL的某个逻辑组合的应用程序已经与API紧密耦合,因此违反了REST尝试解决的原则,即客户端与服务器API的分离。
如果要以RESTful方式通过PUT更新(子)资源,则必须遵循put的语义,该语义基本上表明接收的有效负载替换了在更新之前在给定URI处可访问的有效负载。
PUT方法请求创建目标资源的状态或用请求消息有效负载中包含的表示定义的状态替换目标资源的状态。
...
POST请求中的目标资源旨在根据资源自身的语义处理封闭的表示,而PUT请求中的封闭表示被定义为替换目标资源的状态。因此,PUT的意图对于中介来说是幂等的和可见的,即使确切的效果仅由原始服务器知道。
关于部分更新,RFC 7231声明可以通过使用@Alexandru建议的PATCH
或直接在子资源上发出PUT
请求来进行部分更新,其中有效负载将子资源的内容替换为有效负载中的内容。 。对于包含子资源的资源,这会影响部分更新。
通过将具有与较大资源的一部分重叠的状态的单独标识的资源定向,或者通过使用为部分更新专门定义的不同方法(例如,[RFC5789]中定义的PATCH方法),可以进行部分内容更新。
因此,在您的情况下,您可以通过PUT
操作直接将更新的书籍集合发送到类似于替换旧集合的.../author/{authorId}/books
资源。对于撰写了许多出版物的作者来说,这可能不会很好地扩展,PATCH
可能更可取。但请注意,PATCH
需要原子和事务行为。所有行动都成功或没有成功。如果在操作过程中发生错误,则必须回写所有已执行的步骤。
关于你对进一步文献的要求,SO不是一个正确的问题,因为有一个自己的非主题关闭/标志的原因。
我会选择第一个选项并使用单独的方法,而不是在通用PUT
中填写所有逻辑。即使你依赖的是API而不是数据库,这只是第三方依赖,你应该能够在任何时候切换,而不必重构太多的代码。
话虽这么说,如果你要允许一次更新大量书籍,那么PATCH
可能是你的朋友:
查看RFC 6902(定义Patch标准),从客户端的角度来看,API可以像
PATCH /authors/{authorId}/book
[
{ "op": "add", "path": "/ids", "value": [ "24", "27", "35" ]},
{ "op": "remove", "path": "/ids", "value": [ "20", "30" ]}
]
从技术上讲,解决方案1落实。
REST API URL由资源(以及标识符和过滤器属性名称/值)组成。它不应包含动作(动词)。使用动词鼓励创建愚蠢的API。
例如。我知道一个真实的生产API,希望你
POST
上做/getrecords
以获得所有记录POST
做/putrecords
以添加新记录选择解决方案2的原因不是技术性的。
可以使用PATCH语义,但是使用HTTP PATCH(tools.ietf.org/html/rfc5789)语义来设计URL(而不是Alexandru Marculescu建议的JSON PATCH语义)。
当然,JSON PATCH语义可用于设计PATCH请求的主体。