如果资源不存在,HTTP PUT 是否应该创建资源?

Should HTTP PUT create a resource if it does not exist?

假设有人对我的端点执行 PUT 请求:

/resources/{id}

但是我的 PostgreSQL 数据库中没有存储具有给定 ID 的资源。

根据 RFC 2616,如果我有能力,我应该创建资源:

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

可以使用提供的 ID 创建资源吗?由于在数据库插入时手动分配 ID 不是最佳做法。

如果无法创建资源,我是否应该 return 出现 404 错误?

首先,您使用的是 过时的 文档:RFC 2616 现在不再适用,任何使用此类文档作为参考的人都应该 立即停止.

引用 Mark Nottingham 在撰写本文时 co-chairs IETF HTTP 和 QUIC 工作组:

Don’t use RFC2616. Delete it from your hard drives, bookmarks, and burn (or responsibly recycle) any copies that are printed out.

旧的 RFC 2616 已被以下文档所取代,这些文档共同定义了 HTTP/1.1 协议:

如果您正在寻找方法、状态代码和 header 定义,那么 RFC 7231 是您应该参考的文档。


话虽如此,让我们回到你的问题。

Should HTTP PUT create a resource if it does not exist?

视情况而定。

但是,如果您的应用程序代表客户端生成资源标识符,正如您在问题中提到的,那么您应该使用 POST instead of PUT 来创建资源。

下面引用了 PUT 方法定义的某些部分。最后一句话似乎与你最相关(highlight 是我的),支持我上面刚刚提到的内容:

4.3.4. PUT

The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload. [...]

If the target resource does not have a current representation and the PUT successfully creates one, then the origin server MUST inform the user agent by sending a 201 (Created) response. If the target resource does have a current representation and that representation is successfully modified in accordance with the state of the enclosed representation, then the origin server MUST send either a 200 (OK) or a 204 (No Content) response to indicate successful completion of the request. [...]

Proper interpretation of a PUT request presumes that the user agent knows which target resource is desired. A service that selects a proper URI on behalf of the client, after receiving a state-changing request, SHOULD be implemented using the POST method rather than PUT. [...]


Should I return a 404 error if the creation of the resource is not possible?

这似乎是要返回的准确状态代码,因为没有找到所请求资源的表示:

6.5.4. 404 Not Found

The 404 (Not Found) status code indicates that the origin server did not find a current representation for the target resource or is not willing to disclose that one exists. [...]


现在,为了完整起见,请在下面找到有关 POST 方法定义的一些相关引用,这些引用应该用于在您的问题中描述的场景中创建资源:

4.3.3. POST

The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics. For example, POST is used for the following functions (among others):

[...]

  • Creating a new resource that has yet to be identified by the origin server;

[...]

If one or more resources has been created on the origin server as a result of successfully processing a POST request, the origin server SHOULD send a 201 (Created) response containing a Location header field that provides an identifier for the primary resource created and a representation that describes the status of the request while referring to the new resource(s).

虽然提供了 201 status code indicates that a new resource has been created, the Location header indicate where the newly created resource is located. If no Location header,但客户端应该假设资源是由有效请求 URI 标识的:

6.3.2. 201 Created

The 201 (Created) status code indicates that the request has been fulfilled and has resulted in one or more new resources being created. The primary resource created by the request is identified by either a Location header field in the response or, if no Location field is received, by the effective request URI. [...]

简而言之,这取决于您要存储的负载是否违反服务器对资源的任何限制。

总的来说,我会说它应该尝试它,因为客户端明确表达了他在目标 URI 中存储该特定表示的意图。服务器应该在之前执行约束检查!通常,在真实的 REST 场景中,客户端应该使用服务器提供的 URI,而不是自己选择任何 URI。因此,服务器应该控制其命名空间,因此默认情况下不建议使用 PUT 创建资源。

话虽如此,由于 PUT 是幂等的,而 POST 不是,一些客户可能希望从中受益 属性。这里 POST-PUT creation pattern 已经发展,其中客户端尝试通过 POST 创建新资源,直到它通过响应中的 Location header 收到确认,然后尝试通过 PUT 更新该资源的状态。这样,客户端可以确保在出现传输问题时只创建一次表示。根据立场,有些人可能会认为资源的实际更新是实际的资源创建,尽管客户端事先收到了相应的 link,但事实并非如此。

请注意,如果服务器配置为为某些 URI 端点提供特定表示,则服务器也有权将表示转换为不同的东西。考虑通过 PUT 将图像上传到 URI,然后服务器将图像嵌入 HTML 页面

这里有两个问题:1) PUT 是否应该尝试创建资源以及 2) 如果不能创建会怎样。

1) @cass 链接的 RFC 说 https://www.rfc-editor.org/rfc/rfc7231#section-4.3.4:

The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload. A successful PUT of a given representation would suggest that a subsequent GET on that same target resource will result in an equivalent representation being sent in a 200 (OK) response.

进一步,Mozilla的文字https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT

The HTTP PUT request method creates a new resource or replaces a representation of the target resource with the request payload.

此外,来自原始 RFC(已被上述测试取代)https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

这有点轶事,但 Kubernetes API 也仔细区分了这一点,并告知 PATCH 的用户他们是否真的想要更新:https://kubernetes.io/docs/reference/using-api/api-concepts/#api-verbs

For PUT requests, Kubernetes internally classifies these as either create or update based on the state of the existing object. An update is different from a patch; the HTTP verb for a patch is PATCH.

2 就“如果失败会发生什么”而言,我认为代码取决于出了什么问题:

  • 400:由于负载错误无法创建
  • 409:由于冲突无法创建 - 例如输入 JSON 中的某些字段对其进行了一些全局唯一性检查
  • 502/3 - 无法创建,因为它试图调用数据库但已死

我不确定 404 是否是最好的代码,因为它没有告诉用户任何关于 为什么 .