【问题标题】:Patterns when designing REST POST endpoint when resource has a computed property当资源具有计算属性时设计 REST POST 端点时的模式
【发布时间】:2022-11-02 02:13:36
【问题描述】:
我有一个资源,例如一个'书'。
我想创建一个 REST POST 端点以允许消费者创建一本新书。
但是,有些属性是必需的和计算的通过 API,而其他人实际上是按原样拍摄的
Book
{
name,
color,
author # computed
}
让我们说作者是根据书名在 API 中以某种方式计算的。
我可以想到这些解决方案每个都有其缺点:
- 强制消费者提供作者并过滤它(不考虑作为输入)#不好,因为作者被更改的原因非常不可预测
- 允许用户提供作者#同样的问题
- 不允许用户提供作者并在用户提供时显示异常
最后一个解决方案似乎是最明显的一个。我能看到的主要问题是它不一致,消费者稍后在 GET 请求中看到作者可能会很奇怪。
我希望我的 POST 端点尽可能具有表现力。因此 POST 和 GET 数据传输对象看起来几乎相同。
是否有任何简单、富有表现力和可预测的模式可供考虑?
【问题讨论】:
标签:
api
rest
oop
design-patterns
【解决方案1】:
就我个人而言,我非常喜欢对GET 请求和PUT 使用相同的格式。
这使得客户端可以执行GET 请求,将属性添加到他们收到的对象并立即再次PUT。如果您的 API 和客户端遵循此模式,这也意味着它可以轻松地向 GET 请求添加新属性,而不会破坏客户端。
然而,虽然这是一个很好的模式,但我并不认为对“创造”存在同样的期望。在创建新项目时,通常有很多东西可以减少对属性的要求(例如考虑“id”),所以我通常:
- 为
PUT 和GET 定义一个模式。
- 为
POST 定义一个单独的架构,该架构仅包含用于创建的相关属性。
- 如果用户提供的属性不在架构中,则始终会出现
422 错误。
【解决方案2】:
一些属性是必需的,由 API 计算
根据定义,计算属性既不是必需的,也不是可选的。没有理由要求消费者传递此类属性。
不允许用户提供作者并在用户提供时显示异常
实际上,DTO 不应包含作者属性。消费者可以通过网络发送他们想要的任何东西,但是 API 提供者有责任发布合同 (DTO) 以供消费者正确使用。 API 提供者控制要考虑的属性,并且不应抛出异常,因为消费者可以发送的“坏”属性的数量是无穷无尽的。
因此 POST 和 GET 数据传输对象看起来几乎相同
使相同资源的 DTO 看起来相同并不是目标。在许多情况下,对于同一资源,get-operation 比 post-operation 暴露更多的属性,尤其是在设计领域驱动的 API 时。
是否有任何简单、富有表现力和可预测的模式可供考虑?
如果您希望您的 API 表达计算作者的事实,您可以使用以下端点:
发布 http://.../author-computed-books
获取 http://.../books/1
就个人而言,我不会那样实现,因为它看起来不自然,但是您可以理解。
【解决方案3】:
我希望我的 POST 端点尽可能具有表现力。所以邮政
和 GET 数据传输对象看起来几乎相同。
也许只是记录它而不是依赖像它必须与 GET 端点几乎相同的显式内容。
例如。我的 POST 端点是 POST /number "1011",我的 GET 端点是 GET /number -> 11。如果我没有记录我期望二进制并且我提供十进制,那么没有人会知道并且他们会猜测例如两者都是十进制的。除了文档之外,另一种更明确的方法是更改 GET 的响应以包含基础 {"base":10, value:"11"} 或更改 GET 端点 GET /number/decimal -> 11。
作为计算作者,我不明白你将如何计算它。我的意思是要么一本书已经注册,消费者不应该再次注册它,要么你对它的作者不太了解。如果是后者,那么您可以猜测例如根据标题的谷歌结果,但这将是一个猜测,不一定是真的。消费者数据也是如此,但至少这是消费者提供的。没有确定性。所以对我来说,如果信息的来源很重要,这将是一个复杂的属性,而不仅仅是一个原始属性。像"author": {name: "John Wayne", "source": "consumer/service"} 这样的东西通常也很复杂,因为作者往往有 ID、姓名、其他书籍等。
另一个想法是,如果这对消费者来说很奇怪而不是预期,那么我根本不知道为什么它是一个功能。如果作者猜测是一项服务,那么可能的解决方案是强制属性并添加猜测服务GET /author?by-book-name={book-name},以便他们可以根据需要使用该服务。或者与完全可选的属性相同。这样,您就可以将控制权交还给消费者,决定他们是否要使用此服务。