RESTful 幂等性

2024-02-29

我正在利用 ROA(面向资源的架构)设计一个 RESTful Web 服务。

我正在尝试找出一种有效的方法来保证 PUT 请求的幂等性,在服务器指定资源键的情况下创建新资源。

根据我的理解,传统的方法是创建一种事务资源,例如/CREATE_PERSON。创建新人员资源的客户端-服务器交互分为两部分:

第 1 步:获取用于创建新 PERSON 资源的唯一事务 ID:::

**Client request:**
POST /CREATE_PERSON

**Server response:**
200 OK
transaction-id:"as8yfasiob"

步骤 2:在请求中创建新的人员资源,并使用事务 id::: 保证其唯一性

**Client request**
PUT /CREATE_PERSON/{transaction_id}
first_name="Big bubba"

**Server response**
201 Created             // (If the request is a duplicate, it would send this
PersonKey="398u4nsdf"   // same response without creating a new resource.  It
                        // would perhaps send an error response if the was used
                        // on a transaction id non-duplicate request, but I have
                        // control over the client, so I can guarantee that this
                        // won't happen)

我发现这种方法的问题是,它需要向服务器发送两个请求才能执行创建新人员资源的单个操作。这会产生性能问题,增加用户等待客户端完成其请求的机会。

我一直在尝试讨论消除第一步的想法,例如在每个请求中预先发送交易 ID,但我的大多数想法都有其他问题或涉及牺牲应用程序的无状态性。

有没有办法做到这一点?

编辑::::::

我们最终采用的解决方案是让客户端获取 UUID 并将其与请求一起发送。 UUID 是一个非常大的数字,占用 16 个字节(2^128)的空间。与具有编程思维的人的直觉想法相反,随机生成 UUID 并假设它是唯一值是公认的做法。这是因为可能值的数量如此之大,以至于随机生成两个相同数字的几率很低,几乎是不可能的。

需要注意的是,我们让客户端从服务器请求 UUID(GET uuid/)。这是因为我们无法保证客户端运行的环境。如果存在诸如在客户端上播种随机数生成器之类的问题,那么很可能会发生 UUID 冲突。


您在创建操作中使用了错误的 HTTP 动词。RFC 2616 https://www.rfc-editor.org/rfc/rfc2616指定操作的语义POST and PUT.

第 9.5 段:

POST方法用于请求 源服务器接受 请求中包含的实体为 资源的新下属 由请求行中的请求 URI 标识

第 9.6 段

PUT方法要求 封闭的实体存储在 提供的请求 URI。

该行为有一些微妙的细节,例如PUT可用于在指定 URL 处创建新资源(如果尚不存在)。然而,POST永远不应该将新实体放在请求 URL 处并且PUT应始终将任何新实体放在请求 URL 处。与请求 URL 的关系定义POST as CREATE and PUT as UPDATE.

根据该语义,如果您想使用PUT要创建一个新人,应该创建于/CREATE_PERSON/{transaction_id}。换句话说,您的第一个请求返回的交易 ID 应该是稍后用于获取该记录的人员密钥。你不应该做PUT请求的 URL 不会是该记录的最终位置。

不过,更好的是,您可以通过使用POST to /CREATE_PERSON。这允许您通过单个请求来创建新的人员记录,并在响应中获取新的 ID(也应在 HTTP 中引用)Location标题也是如此)。

同时,REST 准则指定动词不应成为资源 URL 的一部分。因此,创建新人员的 URL 应该与获取所有人员列表的位置相同 -/PERSONS(我更喜欢复数形式:-))。

因此,您的 REST API 变为:

  • 让所有人 -GET /PERSONS
  • 获得单身人士 -GET /PERSONS/{id}
  • 创建新人 -POST /PERSONS主体包含新记录的数据
  • 更新现有人员或创建具有已知 ID 的新人员 -PUT /PERSONS/{id}主体包含更新记录的数据。
  • 删除现有人员 -DELETE /PERSONS/{id}

注意:我个人不喜欢使用 PUT 来创建记录,原因有两个,除非我需要创建一个与来自不同数据集的现有记录具有相同 id 的子记录(也称为“穷人的外键”: -))。

Update:你是对的POST不是幂等的,这是根据 HTTP 规范的。POST will always返回一个新资源。在上面的示例中,新资源将是事务上下文。

然而,我的观点是你想要PUT用于创建新资源(人员记录),并且根据 HTTP 规范,该新资源本身应位于 URL 处。特别是,您的方法的问题在于您使用的 URLPUT是由 POST 创建的事务上下文的表示,而不是新资源本身的表示。换句话说,人员记录是更新交易记录的副作用,而不是更新的直接结果(更新的交易记录)。

当然,通过这种方法PUT请求将是幂等的,因为一旦创建了人员记录并且事务“最终确定”,后续的PUT请求不会执行任何操作。但现在你有一个不同的问题 - 要实际更新该人的记录,你需要制作一个PUT请求不同的 URL - 代表人员记录的 URL,而不是创建该记录的事务。因此,现在您有两个单独的 URL,您的 API 客户端必须知道它们并发出请求才能操作同一资源。

或者,您也可以在事务记录中复制最后一个资源状态的完整表示,并让人员记录更新也通过事务 URL 进行更新。但此时交易URLis出于意图和目的,个人记录,这意味着它是由POST请求放在第一位。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

RESTful 幂等性 的相关文章

随机推荐