设计用于使用 PATCH 执行部分更新的端点

Sli*_*lim 1 rest http

考虑一个名为 的大实体entity。它公开了 100 种不同的更新操作,包括添加和删除属性、更新属性等。

使用单个 URI 和应用程序服务器路由处理的许多情况之间应该优先选择什么:

PATCH /entity/[id]  {"type":"a","key1":"val1","key2":"val2"} or
                    {"type":"b","key3":"val3","key4":"val4"} or ...
Run Code Online (Sandbox Code Playgroud)

...并且使用许多 URI,每个应用程序服务器路由处理一种情况:

PATCH /entity/[id]/a  {"key1":"val1","key2":"val2"}
PATCH /entity/[id]/b  {"key3":"val3","key4":"val4"}
Run Code Online (Sandbox Code Playgroud)

或者也许使用PUT?需要更新(非常)部分实体。

考虑到 REST 合规性、负载均衡器、缓存、KISS 等,最好的方法是什么。任何想法将不胜感激。

cas*_*lin 6

太长了;博士

\n

PATCH只要做得正确,您应该可以使用单个端点支持。

\n

请求PATCH有效负载应包含一组用于修改目标资源的指令。表示这组指令的合适格式是JSON PatchJSON Merge Patch(它们允许修改目标 JSON 文档中的嵌套值)。

\n

对资源状态执行修改

\n

要对资源进行修改,您可以使用PUTPATCH(或两者)。然而,这些方法之间的差异反映在服务器处理请求负载以修改目标资源的方式上:

\n
    \n
  • 在请求中PUT,有效负载是服务器上存储的资源的修改版本。并且客户端请求将存储的版本替换新版本。所以可能并不完全适合部分修改。

    \n
  • \n
  • 在请求中PATCH,请求有效负载包含一组指令,描述应如何修改当前存储在服务器上的资源以生成新版本。这意味着它是对资源执行部分修改的最合适的方法。

    \n
  • \n
\n

为了让事情变得清楚,我在下面整理了一些例子。

\n

使用PUT

\n

例如,考虑一下您正在创建一个 API 来管理联系人。在服务器上,您有一个可以用以下 JSON 文档表示的资源:

\n
{\n  "id": 1,\n  "name": "John Appleseed",\n  "work": {\n    "title": "Engineer",\n    "company": "Acme"\n  },\n  "phones": [\n    {\n      "phone": "0000000000",\n      "type": "mobile"\n    }\n  ]\n}\n
Run Code Online (Sandbox Code Playgroud)\n

假设\xe2\x80\x99s 假设John 已晋升为高级工程师,并且您希望及时更新联系人列表。我们可以使用PUT请求修改此资源,如下所示:

\n
PUT /contacts/1 HTTP/1.1\nHost: example.org\nContent-Type: application/json\n\n{\n  "id": 1,\n  "name": "John Appleseed",\n  "work": {\n    "title": "Senior Engineer",\n    "company": "Acme"\n  },\n  "phones": [\n    {\n      "phone": "0000000000",\n      "type": "mobile"\n    }\n  ]\n}\n
Run Code Online (Sandbox Code Playgroud)\n

使用 时PUT,即使您需要修改资源的单个字段,也必须将资源新状态的完整表示发送到服务器,这在某些情况下可能是不可取的。

\n

来自RFC 7231

\n
\n

4.3.4. 放

\n

PUT方法请求创建目标资源的状态或用请求消息有效负载中所包含的表示所定义的状态来替换目标资源的状态。[\xe2\x80\xa6]

\n
\n

使用PATCH

\n

PATCH然而,除了提到请求有效负载应包含一组描述如何修改资源的指令以及该组指令由媒体标识之外,该方法定义并不强制请求有效负载的任何格式。类型(定义PATCH服务器应如何应用)。

\n

来自RFC 5789

\n
\n

2. 补丁方法

\n

PATCH方法请求将请求实体中描述的一组更改应用于请求-URI 标识的资源。这组更改以称为 \xe2\x80\x9cpatch 文档\xe2\x80\x9d 的格式表示,该格式由媒体类型标识。[\xe2\x80\xa6]

\n
\n

下面列出了一些描述此类更改的合适格式:

\n

JSON 补丁

\n

它表示要应用于 JSON 文档的一系列操作。它在RFC 6902中定义并由媒体类型标识application/json-patch+json

\n

JSON Patch 文档表示一个对象数组,每个对象表示要应用于目标 JSON 文档的单个操作。

\n

JSON Patch 文档的评估从目标 JSON 文档开始,并且操作按照它们在数组中出现的顺序顺序应用。序列中的每个操作都会应用于目标文档,并且生成的文档将成为下一个操作的目标。评估将继续,直到成功应用所有操作或遇到错误情况。

\n

操作对象必须只有一个op成员,其值指示要执行的操作:

\n
    \n
  • add:在目标位置添加值;如果该值存在于给定位置,则将其\xe2\x80\x99s 替换。
  • \n
  • remove:删除目标位置的值。
  • \n
  • replace:替换目标位置处的值。
  • \n
  • move:删除指定位置的值并将其添加到目标位置。
  • \n
  • copy:将指定位置的值复制到目标位置。
  • \n
  • test:测试目标位置处的值是否等于指定值。
  • \n
\n

任何其他值均被视为错误。

\n

话虽如此,修改 John\xe2\x80\x99s 职位的请求可能是:

\n
PATCH /contacts/1 HTTP/1.1\nHost: example.org\nContent-Type: application/json-patch+json\n\n[\n  { "op": "replace", "path": "/work/title", "value": "Senior Engineer" }\n]\n
Run Code Online (Sandbox Code Playgroud)\n

JSON 合并补丁

\n

它是一种使用非常模仿正在修改的文档的语法来描述对目标 JSON 文档进行的更改的格式。它在RFC 7396中定义并由媒体类型标识application/merge-patch+json

\n

处理 JSON 合并补丁文档的服务器通过将提供的补丁的内容与目标文档的当前内容进行比较来确定所请求的确切更改集:

\n
    \n
  • 如果合并补丁包含未出现在目标文档中的成员,则会添加这些成员。
  • \n
  • 如果目标确实包含该成员,则该值将被替换。
  • \n
  • null合并补丁中的值指示目标文档中的现有值将被删除。
  • \n
  • 目标文档中的其他值将保持不变。
  • \n
\n

这样,修改 John\xe2\x80\x99s 职位的请求可以是:

\n
PATCH /contacts/1 HTTP/1.1\nHost: example.org\nContent-Type: application/merge-patch+json\n\n{\n  "work": {\n    "title": "Senior Engineer"\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

底线

\n

当涉及到修改资源的状态时,PUT和的使用PATCH最终取决于您的需求。选择其中之一、另一个或两者。但在支持它们时要坚持标准:

\n
    \n
  • 如果您选择支持PUT,则请求负载必须是资源的新表示(因此服务器将替换资源的状态)。

    \n
  • \n
  • 如果您选择支持PATCH,请使用标准格式的负载,例如 JSON Patch 或 JSON Merge Patch(其中包含一组告诉服务器如何修改资源状态的指令)。

    \n
  • \n
\n

有很多库可以解析 JSON Patch 文档,所以不要重新发明轮子。如果您认为 JSON Patch 太复杂,无法满足您的需求,请使用 JSON Merge Patch。如果您对这两种格式都感到满意,那么没有什么可以阻止您支持这两种格式。

\n

PATCH而且,由于这两种格式都允许您修改 JSON 文档中的嵌套值,因此即使您必须允许修改嵌套值或资源,您也应该可以使用单个端点。

\n

验证资源的状态

\n

如果您担心应用资源后如何保持一致PATCH,我建议您查看此答案。简而言之,建议您将代表您的 API 资源的模型与代表您的域的模型解耦。

\n

因此,在处理PATCH请求时:

\n
    \n
  1. 获取您想要更新的域模型实例。
  2. \n
  3. 将领域模型转换为对应的API资源模型。
  4. \n
  5. 将补丁应用到 API 资源模型。
  6. \n
  7. 将具有更新的 API 资源模型转换回域模型。
  8. \n
  9. 验证域模型的状态:
  10. \n
\n
    \n
  • 如果状态有效,则接受请求并保留对域模型所做的更改。
  • \n
  • 否则,拒绝该请求。
  • \n
\n

您可能还需要参阅RFC 5789以了解有关处理请求时的错误处理的详细信息PATCH

\n

进一步阅读(特别是如果您使用 Java 和 Spring)

\n

如果您使用 Java 和 Spring,您可能想看看我博客中的这篇文章。如果您不使用 Java 或 Spring,那么您可能仍然对本文第一部分中使用 或PUT修改资源的注意事项感兴趣(尽管我很好地总结了这个答案中的主要思想)。PATCH

\n