用于创建资源的 REST API 补丁方法

Posted

技术标签:

【中文标题】用于创建资源的 REST API 补丁方法【英文标题】:REST API Patch method for create resource 【发布时间】:2019-12-02 11:41:51 【问题描述】:

使用 JSONAPI 1.0 标准设计 API 没有 PUT 方法。创建资源只有 POST 方法,部分更新只有 PATCH 方法。我们有用户可以向服务器发送请求的用例,如果资源不存在,则必须创建否则更新。 RFC 将此类方法描述为 PUT。接下来引用提到的 RFC 5789 标准 PATCH 有信息:

"如果 Request-URI 没有指向现有资源,服务器可以创建一个新资源, 取决于补丁文档类型(是否可以逻辑修改空资源)和权限等。”

使用 PATCH 方法来更新和创建资源是个好主意吗?应该使用哪个标准来支持 PUT 和 PATCH 方法(可能是 OpenApi)?

如何解读 RFC 描述?

最好的问候

【问题讨论】:

【参考方案1】:

我们有用户可以向服务器发送请求的用例,如果资源不存在则必须创建否则更新。

在这种情况下,正确的答案几乎肯定是 POST 您对集合资源的请求,并让服务器找出“正确”的做法。

可以通过向代表资源集合的 URL 发送 POST 请求来创建资源。

使用PUT 创建资源假定客户端 可以/应该猜测新资源的正确标识符应该是什么。如果我们不授予客户端该权限/控制权,则请求需要改为使用稳定的 target-uri,并且服务器会计算对其他资源的副作用。

在 JSON:API 中,服务器可以控制新项目的 URI 选择。

POST /photos HTTP/1.1
Content-Type: application/vnd.api+json

...

HTTP/1.1 201 Created
Location: http://example.com/photos/550e8400-e29b-41d4-a716-446655440000

如果 API 支持 PUT 语义,则等效更改看起来 像

PUT /photos/550e8400-e29b-41d4-a716-446655440000 HTTP/1.1
Content-Type: application/vnd.api+json

HTTP/1.1 201 Created

但 JSON:API 已决定 PUT isn't interesting yet。在字里行间,作者决定应该保留PUT,直到更多的实现证明他们理解 HTTP 规范。

因此,您可以对集合进行 POST 以进行创建,并在项目上进行 PATCH 以进行部分或完全替换。

这反过来意味着如果客户端不/不能知道资源已经存在,它应该 POST 到集合。反过来,服务器应该知道资源可能已经存在,并做一些明智的事情(替换资源的表示,将客户端重定向到资源等)。服务器如何实现这一点将是一个实现细节。

查看互联网处理 REST HTTP 方法我从未见过 PATCH 可用于资源创建,因此我很惊讶 JsonApi 放弃 PUT 方法。

PATCH 当然可以用于资源创建——参见RFC 5789

如果Request-URI不指向现有资源,服务器可以创建新资源,具体取决于补丁文档类型(是否可以在逻辑上修改空资源)和权限等。

这是一个不常见的选择,因为 PUT 语义更适合该用例。选择支持 PATCH,但不支持 PUT,这很奇怪。

我很惊讶 JsonApi 放弃了 PUT 方法

我也很惊讶。

registering a new profile 或许可以解决您的疑虑,鼓励社区为您需要的语义采用通用模式。

【讨论】:

是的,事情对我来说很清楚,但它与下面的 Roman Vottner 所写的相反。使用 JsonAPI 并对资源有 100 个 POST 请求,其中一半资源存在,我们最终以 150 个请求结束。其中 50 个将被服务器拒绝使用 PATCH。研究 REST HTTP 方法的 Internet 处理我从未见过 PATCH 可用于资源创建,因此我很惊讶 JsonApi 放弃了 PUT 方法。在他们的文档的另一面,没有我们可以使用 PATCH 创建的信息以及响应的外观。 @Papub 我不认为 Voice 一定要表达与我不同的东西。他/她明确地描述了对appliction/vnd.api+json 的看法,而我的帖子包含了关于实践中使用的两种常见补丁方法的更多一般信息。媒体类型只是定义了有关如何将资源状态转换为可以发送给另一方并由另一方处理的内容的语法和语义。相同的数据通常可以以多种形式表示,即一些 XML 或 JSON 俚语。就一种类型达成一致并相应地格式化数据是内容类型协商的一部分 Roman - 你是对的,但我认为 JsonApi 不仅描述了语法和语义,还描述了如果我们想使用这个标准,我们必须尊重的其他事情。否则,我们可以创建解决讨论的 PUT 方法,并在请求中添加 appliction/vnd.api+json 以由服务器端正确解释。【参考方案2】:

根据Postel's law 应该是be conservative in what you do, be liberal in what you accept from others

PATCH 使用的两种常见媒体类型是 application/json-patch+json(又名 JSON 补丁)和 application/json-merge-patch+json(又名 MergePatch)。

MergePatch 定义了几个规则来确定是否需要添加、删除或更新部件。规范定义了接收到的那种类型的请求需要通过调用function that takes in two arguments、目标资源和接收到的表示来处理。目标本身可能是 JSON 值或未定义。如果该资源尚不存在,则它是未定义的,并将导致接收到的补丁文档中的所有值都添加到尚未定义的资源中。这基本上就是你的资源创建。

与 MergePatch 相比,JSON Patch 被指定为仅对 JSON 文档进行操作。没有提到在没有可用资源的情况下需要如何应用补丁。如果您查看操作是提供的,这在某种程度上是有道理的,例如testremovereplacemove,只有在原始 JSON 文档中有对应的可用时才有效。这种媒体类型非常接近实际的PATCH 定义,因为客户端发送一组指令,这些指令先前由客户端计算,需要由服务器自动应用。要么应用所有更改,要么不应用任何更改。这里客户端应该已经预先获取了目标资源的当前状态,否则它将无法计算必要的更改以将当前表示转换为所需的表示。因此,只有在已经有可用资源的情况下,应用该媒体类型的表示才有意义。如果客户端看到还没有可用的目标资源,它可以简单地使用POST 然后创建资源。如果客户端发送仅包含 add 操作的补丁文档,我将创建一个 JSON 表示并相应地添加所有字段。

如您所见,关于如何在 HTTP 中完成 PATCHing,有两种不同的看法。一种非常接近几十年来如何在软件工程中完成修补的原始想法,而另一种方法是部分更新远程资源的更实用的方法。您使用或支持哪一个(最好是两者)是您的选择。

【讨论】:

感谢您的回答。正如我在提供的 RFC 中看到的,目标是 Json Document? “JSON 合并补丁文档描述了要对目标 JSON 文档进行的更改”。因此,如果我们接受 application/vnd.api+json 并且我们在 application/vnd.api+json 中做出响应,我们假设我们的目标是 json 文档,对吗?我理解正确吗? 上述两种媒体类型,JSON Patch 和 MergePatch,都对 JSON 表示进行操作。类似于 JSON PATCH 但用于 XML 文档的媒体类型是 XML Patch。 IANA 列出了 3 种与修补相关的其他媒体类型,尽管其中一种看起来类似于 XML Patch,而另外两种用于 YANG 数据节点。如果您有特殊需求,您还可以定义自己的媒体类型并向 IANA 注册,最终提供库以更直观地使用您的补丁方法 但是“JSON:API 需要使用 JSON:API 媒体类型 (application/vnd.api+json) 来交换数据。”因此我们不能使用其他媒体类型。您提供了常见的媒体类型,但我也想知道标准 PATCH 调用是否可以创建资源。使用上面列出的媒体类型我认为这是可能的,但是没有这些媒体类型的 PATCH 呢? RFC 5789 基本上将该决定留给更具体的“实施”。通常,在处理传入请求时尽可能多地允许(即通过 PATCH 创建资源)通常是有益的,但在发送响应时要严格(参见 Postel 定律)。但是,服务器/API 支持多种不同的媒体类型以覆盖更广泛的受众也是有益的。当然,如果服务器只支持一组有限的媒体类型,除非添加特殊支持,否则客户端将无法与服务器交互

以上是关于用于创建资源的 REST API 补丁方法的主要内容,如果未能解决你的问题,请参考以下文章

Magento 2 REST API入门

API Gateway 中控制和管理对 REST API 的访问

创建 REST API 时使用 Laravel 嵌套动态资源控制器的正确方法

S3 REST API 和 POST 方法

什么是REST API?

WCF REST 到 Web API [关闭]