RESTful的POST方法能够实现幂等性吗?

9

我正在设计一个管理收藏夹的RESTful API。每个收藏资源可以包含两个项目的详细信息,这些项目被视为收藏的一部分。

 HTTP POST /favorites

 {  "item1" : "ball",
    "item1-ID" : "1",
    "item2" : "bat",
    "item2-ID" : "2"
 }

请原谅简陋的JSON负载。然而,重点在于POST的语义。
以上POST方法创建了一个新的收藏资源(其中包含一个球(ID 1)和一个棒(ID 2))。
我的问题是关于当相同的POST请求被发送两次时预期行为是什么。第一次请求将创建一个喜爱的资源(如预期所示)。当第二个请求被发送时应该发生什么?
1)使用错误代码409信号
2)使用成功代码201信号
1)不是幂等的(因为POST是),而2)使POST成为幂等的。
哪种方法是正确的?

其中一个标签是Java...这与Java无关。 - Krusty the Clown
如果我是使用这个程序的程序员,我宁愿出现错误。我可以选择忽略错误,但我无法知道成功意味着什么不同的含义。 - markspace
这是众多PUT vs POST资源之一;其中一个说“PUT是幂等的,而POST不是”。 - Stephen P
5个回答

4
您的思维方式有误。如果POST创建了一个新资源,那么该资源应该有一个标识它的URL(例如:http://.../favorite/1)。当第二个相同负载的POST发生时,是否会创建一个新资源?如果是,则您将拥有两个具有唯一URL的单独资源。如果您的应用程序不会创建新资源,则第二个POST将返回与第一个相同的URL。 编辑 RFC7231POST部分并未禁止以幂等方式实现。但它提供了一些指导:
  • 如果POST创建了一个新资源,则应发送一个包含标识所创建资源的Location头的201(已创建)响应
  • 如果POST的结果等效于现有资源,则服务器可以通过返回带有适当Location头的303(参见其他)来将UA重定向到现有资源
POST不需要更改资源的状态。但是,GETHEAD必须是幂等的。我不知道有任何一种方法需要更改服务器上的状态。
就个人而言,我将创建资源的POST方法实现为返回一个303重定向到资源的规范URL,这是一种惯例。我不知道RFC区分了用于资源创建和重用响应的状态代码的好处是,它消除了在POST响应中包含创建的资源以及在规范URL上的GET所需的内容。将响应缓存到可能存在的任何中间缓存中。它还允许实现智能客户端,这些客户端不会检索响应,而是使用(来自Location头的)规范URL。

在您的情况下,我认为如果一个喜欢有它自己的URL,那么我会采用303方法;如果没有,那么201会很奇怪。如果有非常好的理由来区分响应中的创建和重用案例,那么在创建新资源时返回201,重用时返回303是恰当的。但我不会返回409,除非您的应用程序要求您拒绝创建已经存在的资源。


我的问题是关于实现是否允许POST方法具有幂等性?我的应用程序认为第二个请求是多余的(因为所需项目已添加为收藏夹)。因此,我想了解返回201是否可接受(因为这意味着可以发送多个POST请求而不会导致服务器上的任何新状态更改,使POST方法具有幂等性)。 - Sirish Renukumar

1

在我看来,正如你所描述的那样,如果能够创建新的收藏夹将会很好。但是这对于几乎任何应用程序来说都感觉很奇怪。如果:

  • 您的收藏定义为不可重复,如果它们是精确的副本
  • 您可以发送一个带有ID的收藏夹(这不是个好主意,因为它基于客户端),并根据ID插入或更新
  • 您可以发送一些其他唯一字段,并使用其定义“创建”或“更新”(即“收藏名称”或“菜单中的位置”)

正如您所看到的,这完全取决于您的应用程序。

也许您可以从我之前写的这篇文章中获得一些想法:深入了解各种REST API ,别错过底部的总结。


0
第二个请求发送时应该发生什么?
这完全取决于您的实现。比如说,如果您的控制器中有一些逻辑,检查数据中是否存在ID 1和ID 2,然后更新值,否则创建新记录。整个过程不依赖于POST请求。
另外,成功的HTTP响应代码是200而不是201。
您当前的方法不正确,这个问题也不是一个JAVA问题。

3
"201"是HTTP成功响应代码,表示"已创建",在REST场景中是一个有效的响应代码。关于Java的评论应该作为注释而不是正式内容,建议您删除它并将其作为注释添加到问题中。另外建议完全删除"your current approach..."这句话 ;)。 - jjmontes

0

一般来说,这取决于您如何设计底层系统。在大多数情况下,POST会创建一个新的资源(是的,有时您使用POST而不创建任何新内容。例如,您有一个用例,您想要在非常长的参数列表上进行搜索,GET可能不是正确的方式!这是完全可以接受的,不违反REST原则。)

针对您的具体情况进行评论,

当发送第二个请求时应该发生什么?

你应该问自己以下问题:
1. 对于你的系统来说,是否存在重复请求(具有相同的负载/标头/时间戳等)是一个“不同”的请求?如果是,请像处理非重复请求一样正常处理。我假设接收请求的下一个系统也将其视为新请求,否则可能会失败并标记错误。例如,MySQL 可能会抛出重复主键错误(参考:错误代码:1062。重复条目 'PRIMARY'
2. 如果问题 1 的答案是否定的,则此请求的重复发生是否是客户端的预期行为?(例如,如果移动设备间歇性地获取网络,则往往会收到重复请求。)如果是,请返回 200 OK。请注意幂等性。
3. 如果问题 2 的答案是否定的,则使用客户端可以处理和相应的自定义错误代码抛出 409 冲突。如果可能的话,我还将调查客户端的不良行为并修复它。

0

如果响应的状态码为200,则重复的幂等POST请求应该返回先前创建的资源。幂等执行场景


链接已经失效,这是新的链接:https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-02#section-2.6 - caiolopes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接