在 REST 中,POST 还是 PUT 最适合 upsert 操作?

Posted

技术标签:

【中文标题】在 REST 中,POST 还是 PUT 最适合 upsert 操作?【英文标题】:In REST is POST or PUT best suited for upsert operation? 【发布时间】:2013-08-30 12:06:47 【问题描述】:

我在服务器中为客户端保留了一个键值对存储。如果用户发送密钥“k1”,那么我将它插入到数据库中。这被认为是POST 还是PUT

我还有另一个操作,删除所有现有密钥并添加新密钥。这是POST 还是PUT,因为它会清除记录并添加新记录。

【问题讨论】:

【参考方案1】:

如果用户发送密钥“k1”然后我将它插入到数据库中。这被认为是 POST 还是 PUT。

根据HTTP specification:

PUT 方法请求将封闭的实体存储在提供的 Request-URI 下。如果 Request-URI 引用一个已经存在的资源,封闭的实体应该被认为是在源服务器上的一个修改版本。如果 Request-URI 不指向现有资源,并且该 URI 能够被请求用户代理定义为新资源,则源服务器可以使用该 URI 创建资源。

因此,我认为使用 PUT 进行插入或更新是完全合法的,前提是在这两种情况下都事先知道 URI。如果您将密钥用作 URI 的一部分(如 http://www.somewhere.com/resources/k1 中的 k1),则应该是这种情况。然而,为了实现理想的 RESTful,对同一 URL 的 GET 还应该允许您下载资源。

我还有另一个删除所有现有密钥并添加新密钥的操作,这是 POST 还是 PUT,因为它会清除记录并添加新的。

我认为这个操作不能被认为是 RESTful,因为它做了两件事。它似乎提供了一个宏来满足特定客户端的需求,而不是简单地访问数据。一个标准的 RESTful 设计将是

    通过向父 URL 发送 GET 来获取密钥列表。在上面的例子中,那将是http://www.somewhere.com/resources; 通过将 DELETE 发送到 http://www.somewhere.com/resources/k1 来删除每个密钥; 通过向 http://www.somewhere.com/resources/k2 发送 PUT 添加替换。

不太明确,但我认为通过向http://www.somewhere.com/resources 发送单个 DELETE 请求来删除所有资源也是合法的。

【讨论】:

somewhere.com/resources 上的 DELETE 不能替代第 1 步和第 2 步吗? 1,2,3。这就是为什么我觉得这些东西有点过时了。如果我必须同时删除 100 个东西,我应该发出 100 个 DELETE 请求吗?我觉得单个套接字连接或类似的东西应该只根据事件工作。 点赞!那么您对 ​​PATCH 请求有何看法?可以用于这种情况吗 所以如果PUT 将创建或更新......为资源创建POST 路由有什么意义 POST 如果 URI 未知,即使用 FactoryPattern 创建 UUID 或构建资源所需的其他操作(稍后也可能是 URI 的一部分)时,应使用与代码 201 结合使用。PATCH 也可以替代PUT,但通常仅用于部分更新/修补资源 - 应保留未提及的信息。【参考方案2】:

upsert 操作背后的想法是客户端拥有关于/决定数据结构的信息,并使用键值发送数据。所以 upsert 操作的请求模型与包含 key 的更新操作非常相似,如下例所示:

/customers/jimmy

更新现有记录的预期方法是 PUT。所以你的选择应该是 PUT。

POST 通常用于插入具有全新内容的新记录,如下例所示:

POST /customers HTTP/1.1
Content-Type: ...
Content-Length: ...
Host: server.yourdomain.com
Accept: ...
User-Agent: ...

id      jimmy
name    jimmy
Occupation   ***er

因此,在您的情况下,您不需要任何 POST 操作,因为用于 upsert 操作的 PUT 也涵盖了这一点。

关于 upsert 的关键问题是,您在 upsert 操作方面信任客户的可能性有多大。如果客户希望使用现有键插入新记录,会发生什么?在您的情况下,您应该将此请求作为更新处理,因为插入和更新请求都来自同一个 api,并且您有一条现有记录。这是关于设计的问题。

【讨论】:

【参考方案3】:

Polly Shaw 的回答是正确的,但我想提一下,鉴于消息很可能不完整(在尚未创建资源时缺少 ID),PATCH 动词将是稍微正确一点。

https://www.rfc-editor.org/rfc/rfc5789

这是非常精细的调整。

【讨论】:

【参考方案4】:

如果 upsert 的定义是新记录与现有记录的混合(要更新)。

参考:https://restfulapi.net/rest-put-vs-post/

PUT 需要是幂等的。这意味着如果您第二次 PUT 相同的有效负载,则不应更改系统状态。

如果预期的有效负载是新的和现有的混合,并且预期的行为是第二​​次创建更多新记录,那么“upsert”似乎会与 POST 更紧密地对齐。

我们努力创建容错 API。如果你不能使 PUT 幂等并且他们必须使用它,他们可能会破坏系统。另一方面,POST 预计不会是幂等的,因此如果您在有效负载中(一遍又一遍)发送仅更新数据(即使这在技术上违反了 POST 的幂等性规则,因为它没有通过以下方式更改系统状态)在后续调用中添加记录)系统(可能)不会损坏。

规范说 PUT“可以”添加新项目并且“必须”是幂等的 它说 POST “必须”添加新项目并且不是幂等的

如果您真的想实现 upsert,两者都不是完美的,但如果错误导致 PUT 上的损坏 API 是罪魁祸首(它应该是幂等的),而 POST 上的损坏是“我告诉过你的”。

我还想考虑 API 使用者会寻找什么。通常,在新屏幕上工作的 UI 开发人员会寻找添加用户在 UI 中添加的记录。他会先寻找一个 POST,然后发现它也处理等式的 PUT 方面。

所以,两者都不是,但如果必须选择,请选择 POST。

【讨论】:

这个答案没有意义。 Upsert 是幂等的。它第一次创建或更新资源。之后每次它什么都不做。【参考方案5】:

根据MDN Web Docs:

HTTP PUT 请求方法创建一个新资源或用请求负载替换目标资源的表示。

PUTPOST的区别在于PUT是幂等的:调用 它连续一次或多次具有相同的效果(即没有 副作用),而连续相同的 POST 请求可能有 附加效果,类似于多次下单。

语法

PUT /new.html HTTP/1.1

示例

请求

PUT /new.html HTTP/1.1
Host: example.com
Content-type: text/html
Content-length: 16

<p>New File</p>

回复

如果目标资源没有当前表示并且 PUT 请求成功创建一个,那么源服务器必须 通过发送201 (Created) 响应通知用户代理。

HTTP/1.1 201 Created 
Content-Location: /new.html

如果目标资源确实具有当前表示并且 表示根据状态成功修改 所包含的表示,然后源服务器必须发送 200 (OK) 或 204 (No Content) 回复 表示请求成功完成。

HTTP/1.1 204 No Content 
Content-Location: /existing.html

【讨论】:

【参考方案6】:

如果你混合所有东西,你可能没有在做 REST。从RESTful Web services: The basicsPOSTPUT有不同的使用场景:

To create a resource on the server, use POST.
To retrieve a resource, use GET.
To change the state of a resource or to update it, use PUT.
To remove or delete a resource, use DELETE.

因此,将POST 视为向博客发布新票证,而将PUT 视为更改现有值。

删除应该作为 DELETE 动词的独特操作来完成。因为在更新之前“全部删除”听起来不是一个好主意。

【讨论】:

我确实认为在某些情况下“更新插入”是有效的,例如标记用户是否“查看”了资源,即我希望这个“标记/检查/勾选”而不管它之前的状态 5 反对票的答案有什么问题?调用者可以调用 get,然后根据结果放置或发布。 @RuntimeException 我猜这是因为,同样的事情在问题中被告知并在它被更新时询问了不同的场景(当一个端点应该更新资源但如果该资源不存在则创建)。因此,它没有回答问题,而是讲述了已经很清楚的事情。 @RuntimeException,也有点误导将 HTTP 动词等同于 CRUD。 PUT 也可以创建。 PATCH 是另一种更新方式。 @RuntimeException 我认为这个答案没有用的另一个原因是使两个顺序操作具有幂等性非常困难。另一个客户端可能会介于 GET 和 PUT 之间并搞砸你的状态。我认为您希望将 upsert 的实现尽可能靠近最终数据库。单独的 GET/PUT 或 GET/POST 我们不太可能在高度并行的系统中工作。

以上是关于在 REST 中,POST 还是 PUT 最适合 upsert 操作?的主要内容,如果未能解决你的问题,请参考以下文章

REST模式中HTTP请求方法(GET,POST,PUT,DELETE)

REST中 @POST & @PUT 区别

按照惯例应该返回哪些 REST PUT/POST/DELETE 调用?

REST POST PUT差别

Put和Post in Rest之间的区别[重复]

尽管有 AllowAny 权限,django-rest-framework 在 POST、PUT、DELETE 上返回 403 响应