Create, Update 和 HTTP 幂等性

译自原文:https://stormpath.com/blog/put-or-post
原作者:Stormpath 团队

2016/5/12更新:观看 Stormpath Hazlewood 的介绍 REST + JSON API 设计最佳实践.

别忘了, Stormpath 无需代码保护您的 API 的认证

创建、更新和 HTTP 幂等性

对于构建基于 REST 的 api 的开发人员来说,有大量的错误信息和关于何时使用 http 发布和何时使用 http POST 的一些可理解的混淆。有人说 POST 应用于创建资源,PUT 应用于修改资源. 还有人说 PUT 应用于创建资源 POST 应用于修改资源。两者都不太正确。

通常,开发人员认为每个 HTTP 方法都与 CRUD 操作有 1:1 的关系:

CRUDHTTP
CreatePOST
ReadGET
UpdatePUT
DeleteDELETE

这可以是真的,特别是GET和DELETE,但当涉及到哪些 HTTP 方法应该与创建和更新关联时,答案归结为 幂等性

幂等性

幂等性HTTP 规范 中的一个重要概念,无论执行同一请求多少次,幂等 HTTP 请求都将导致服务器的相同状态。 GETHEADPUT,和 DELETE 都具有此属性,但 POST 没有。

为了帮助说明幂等性,我们将使用一个帐户集合(*/accounts),为了简洁起见,我们假定,每个帐户资源有三属性称为givenNamesurname,和 status.

假设您使用 HTTP PUT 方法提交更新请求。在body里,你把 givenName 值定为 “John”,”surname” 值定为 “Smith”。然后,您提交另一个请求 HTTP PUT请求,这次设置 “givenName” 为 “Johnny”。这是幂等吗?不。为什么?因为其他请求 可能 在我们两个请求之间更改了帐户资源的服务器状态。例如,在两个请求之间,”status” 可能被更改为 “blocked”。我们的示例请求无法保证服务器上帐户的状态在重复时是 _相同的_。

请求:

1
2
3
4
5
HTTP/1.1 PUT /accounts/abcdef1234
{
"givenName": "John",
"surname": "Smith"
}

在我们的两个请求之后可能的帐户状态(由于其他请求的副作用):

1
2
3
4
5
{
"givenName": "John",
"surname": "Smith",
"status": "enabled"
}

或者

1
2
3
4
5
{
"givenName": "John",
"surname": "Smith",
"status": "disabled"
}

引述 Dino Chiesa,“PUT 意味着放入一个资源 - 将任何在给定的 URL 可用的资源用不同的资源完全替换。” 使用 “PUT” 请求时, 必须将可用的属性 / 值(而不仅仅是要更改的) 全部 发送。如果我们想发送“disabled”的状态, 而不是 “givenName” 和 “surname”, PUT调用将是幂等的,且消除副作用的。幂等性是 HTTP 规范的基本属性, 必须坚持以保证 web 的互通性和规模化。

最后, 我们应该指出, HTTP 幂等性 仅适用于 服务端 状态, 而不是客户端状态。例如, 客户端可以成功地发送服务器幂等请求, 然后再次发送相同的精确服务器幂等请求, 并遇到错误(例如, 可能是由于服务器中的约束冲突), 这完全是 “合法的”。只要 http 请求导致服务器上相同的状态,就能维持 HTTP 幂等性。

HTTP POST vs HTTP PUT

既然 幂等性 是明确的, 那么在执行创建和更新操作时应该使用哪一种方法?以下是适当使用每个方法时的快速参考。

创建

当您不知道资源标识符时, 使用 POST 创建资源。使用 POST 创建后, 最好的做法是返回 “201 Created” 的状态和新创建的资源的位置, 因为在提交时它的位置未知。这允许客户端稍后访问新资源 (如果需要)。

1
2
3
4
HTTP/1.1 POST /accounts
{
...
}

返回:

1
2
201 Created
Location: https://api.stormpath.com/accounts/abcdef1234

当允许客户端指定新创建的资源的资源标识时, 请使用 PUT。但是请记住, 既然 PUT 是幂等请求, 你必须发送所有可能的值。

1
2
3
4
5
6
HTTP/1.1 PUT /accounts/abcdef1234
{
"givenName": "John",
"surname": "Smith",
"status": "enabled"
}

更新

您可以使用 POST 来发送所有可用值或仅提供可用值的子集:

1
2
3
4
5
HTTP/1.1 POST /accounts/abcdef1234
{
"status": "disabled"
}
Response 200 OK

如果要使用 PUT 更新资源, 则必须是完整的资源更新;必须在 “提交” 请求中发送所有属性值以保证幂等性。

当需要或需要发送 所有 可用值以遵循幂等性要求时, 请使用 PUT, 例如, 在完全更新资源的情况下。

1
2
3
4
5
6
7
HTTP/1.1 PUT /accounts/abcdef1234
{
// 完全更新资源
"givenName": "J",
"surname": "Smith",
"status": "Enabled"
}

您也可以使用 POST 来发送所有值, 服务器状态可能与 PUT 完全相同–它只是不需要由 HTTP 规范来进行。请注意, 幂等性与 HTTP 缓存服务器可缓存的相关性很强, 因此 POST 请求通常不会被缓存。如果您对此缓存副作用感觉OK, 则可以使用 POST 来进行完全更新和部分更新。

POST 是当前唯一的非幂等方法。HTTP 规范是非常通用的, 并且基本上将其声明为 “服务器处理指令”。这意味着在 POST 请求中做任何您想做的事情是 “安全的”。

最后, 我们应该注意到还有另一种方法尚未最后确定的 HTTP 规范, 称为 PATCHPATCH 可替代 POST 进行部分更新。然而, 由于 POST 已经可以简单地处理部分更新, 因此似乎没有足够的理由使用 PATCH,如果 HTTP 规范委员会批准,PATCH 将并入 POST 成为唯一的其他非幂等 HTTP 方法。

Create, Update 和 HTTP 幂等性

https://www.imaegoo.com/2018/put-or-post/

作者

iMaeGoo

发布于

2018-03-27

更新于

2018-03-27

许可协议

CC BY 4.0

评论