RESTful幂等

Chr*_*row 5 rest idempotent

我正在使用ROA(面向资源的架构)设计RESTful Web服务.

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

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

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

**Client request:**
POST /CREATE_PERSON

**Server response:**
200 OK
transaction-id:"as8yfasiob"
Run Code Online (Sandbox Code Playgroud)

第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)
Run Code Online (Sandbox Code Playgroud)

我在这种方法中看到的问题是它需要向服务器发送两个请求才能进行创建新PERSON资源的单个操作.这会产生性能问题,从而增加用户等待客户端完成其请求的机会.

我一直试图找出消除第一步的想法,例如预先发送每个请求的事务ID,但我的大多数想法都有其他问题或涉及牺牲应用程序的无状态.

有没有办法做到这一点?

编辑::::::

我们最终得到的解决方案是客户端获取UUID并将其与请求一起发送.UUID是占用16字节(2 ^ 128)空间的非常大的数字.与具有编程思维的人可能直观地思考的相反,随机生成UUID并假设它是唯一值是公认的惯例.这是因为可能值的数量太大以至于随机产生两个相同数量的几率足够低以至于几乎不可能.

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

Fra*_*nov 4

您在创建操作中使用了错误的 HTTP 动词。RFC 2616POST指定和的操作语义PUT

第 9.5 段:

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

第 9.6 段

PUT方法请求将所包含的实体存储在提供的请求 URI 下。

该行为有一些微妙的细节,例如PUT可用于在指定的 URL 处创建新资源(如果该资源尚不存在)。但是,POST切勿将新实体放在请求 URL 处,而PUT应始终将任何新实体放在请求 URL 处。与请求 URL 的关系定义为POSTasCREATEPUTas UPDATE

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

不过,更好的是,您可以使用POSTto将其作为原子操作来完成/CREATE_PERSON。这允许您通过单个请求来创建新的人员记录,并在响应中获取新的 ID(也应该在 HTTPLocation标头中引用)。

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

因此,您的 REST API 变为:

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

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

更新:你是对的,它POST不是幂等的,并且符合 HTTP 规范。POST始终返回新资源。在上面的示例中,新资源将是事务上下文。

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

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

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