【问题标题】:Designing a PUT and PATCH API Endpoint Having Common Domain Object设计具有公共域对象的 PUT 和 PATCH API 端点
【发布时间】:2022-12-05 02:52:07
【问题描述】:

我有以下域对象,我的 API 端点期望它在其主体中作为 JSON:

final case class MyDomanObj(
  id: Int,
  name: String,
  field1: String,
  field2: Double,
  field3: String,
  field4: String
)

对于 PUT 端点,很明显我可以将它作为一个完整的 JSON 对象并将其解析为这个案例类,但问题来自 PATCH 端点,我可能期望其中有几个字段并且 JSON 解析可能会失败。我可以通过将 MyDomainObj 中的所有字段设为可选来解决此问题,但那将是非常错误的。

关于如何优雅地设计 PATCH 端点的任何想法?我更想知道如何解析 JSON 正文以获取请求中的字段子集。

【问题讨论】:

  • 为什么在另一个案例类中将所有字段都设为可选是错误的?

标签: json scala api patch


【解决方案1】:

它与端点本身无关,而是端点接收的媒体类型和对其执行的 HTTP 操作。 IE。 PATCH 应该被认为是通过在实际源上应用补丁文档来修补一些源代码。该补丁文档包含将文档转换为所需形式所需的实际步骤。

实际上有 2 种流行的基于 JSON 的媒体类型可用,您可以利用它们来满足您的需求。 application/json-patch+jsonapplication/merge-patch+json

前者类似于传统的修补,包括对 JSON 指针寻址的文档的一部分执行的操作。因此,JSON 补丁文档看起来像这样:

[
    { "op": "test", "path": "/a/b/c", "value": "foo" },
    { "op": "remove", "path": "/a/b/c" },
    { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
    { "op": "replace", "path": "/a/b/c", "value": 42 },
    { "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
    { "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
]

后者定义了一组默认行为,即,如果您取消该 JSON 补丁文档中的一个属性,您会将其从文档中删除,而该补丁文档中遗漏的属性虽然存在于原始文件中,但保持不变。根据更改,此处的补丁文档可能看起来与原始文档相似。

请注意,与PUT“更新”相比,这两种形式都不是幂等的。补丁文档只能安全地应用一次,因为此后文档可能以无法进一步解决相应属性的方式发生更改。这需要一些机制,如 ETagIf-Unmodified-Since 以确保没有中间更改。

PATCH 本身进一步要求应用到文档的所有更改都是自动应用的。要么应用所有更改,要么根本不应用。因此,这将需要该方法具有类似事务的行为。

在这两种情况下,尽管客户端实际上决定了服务器需要应用的更改,以便将文档转换为所需的形式。这里的区别仅在于补丁文档中如何处理这些转换,以便服务器可以以原子方式执行所有这些转换。

因此,处理您的情况最优雅的方法是依靠内容类型协商并检查您收到的补丁文件类型,然后根据补丁文件的语义及其定义标准应用更改。

【讨论】:

  • 我想你误读了我的问题。我想知道如何通过重新使用相同的域对象来处理 JSON 解析到 Scala 中的案例类以获取 PATCH 端点。