首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >RESTful幂等

RESTful幂等
EN

Stack Overflow用户
提问于 2010-06-02 03:21:00
回答 3查看 5.5K关注 0票数 5

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

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

据我所知,传统的方法是创建一种事务资源类型,如/CREATE_PERSON。用于创建新的人员资源的客户机-服务器交互分为两部分:

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

代码语言:javascript
复制
**Client request:**
POST /CREATE_PERSON

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

步骤2:在通过使用事务id:保证唯一的请求中创建新的person资源

代码语言:javascript
复制
**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)

我在这种方法中看到的问题是,它需要向服务器发送两个请求,以便执行创建新PERSON资源的单个操作。这会造成性能问题,增加了用户等待客户端完成其请求的可能性。

我一直试图找出消除第一步的想法,比如在每个请求中预先发送事务id,但我的大部分想法都有其他问题,或者涉及到牺牲应用程序的无状态状态。

有办法这样做吗?

编辑:

我们最终使用的解决方案是客户端获取一个UUID,并将其与请求一起发送。UUID是一个占用16个字节(2^128)的非常大的数字。与具有编程思维的人可能直觉的想法相反,随机生成一个UUID并假定它是一个独特的值是公认的做法。这是因为可能值的数目太大了,所以随机产生两个相同的数值的几率很低,几乎是不可能的。

一个警告是,我们的客户端正在从服务器(GET uuid/)请求一个UUID。这是因为我们不能保证我们的客户正在运行的环境。如果存在像在客户端上播种随机数生成器这样的问题,那么很可能会发生UUID冲突。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-06-02 03:56:28

创建操作使用了错误的HTTP谓词。RFC 2616指定了POSTPUT操作的语义。

第9.5段:

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

第9.6段

PUT方法请求将封闭的实体存储在所提供的请求-URI下。

这种行为有一些微妙的细节,例如,如果PUT还不存在,可以使用它在指定的URL上创建新的资源。但是,POST不应该将新实体放在请求URL上,而PUT应该始终将任何新实体放在请求URL上。与请求URL的关系将POST定义为CREATEPUT定义为UPDATE

根据这个语义,如果您想使用PUT来创建一个新的人,那么应该在/CREATE_PERSON/{transaction_id}中创建它。换句话说,您的第一个请求返回的事务ID应该是稍后获取该记录的person键。PUT 您不应该对不可能成为该记录的最终位置的URL发出请求。

更好的是,您可以使用POST/CREATE_PERSON作为原子操作来完成这一操作。这允许您使用单个请求来创建新的person记录,并在响应中获取新ID (这也应该在HTTP头中引用)。

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

因此,REST变成:

  • 让所有的人- GET /PERSONS
  • 想要单身- GET /PERSONS/{id}
  • 使用包含新记录的数据的主体创建新的person - POST /PERSONS
  • 若要更新现有人员或使用众所周知的id - PUT /PERSONS/{id}创建具有包含更新记录数据的主体的新人,请执行以下操作。
  • 删除现有人员- DELETE /PERSONS/{id}

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

更新:您是对的,POST不是幂等的,这是按照HTTP的规定。POST将始终返回一个新资源。在上面的示例中,新的资源将是事务上下文。

但是,我的观点是,您希望使用PUT来创建新资源(人员记录),并且根据HTTP,新资源本身应该位于URL。特别是,您的方法失败之处在于,您与PUT一起使用的URL是POST创建的事务性上下文的表示,而不是新资源本身的表示。换句话说,person记录是更新事务记录的副作用,而不是它的直接结果(更新的事务记录)。

当然,使用这种方法,PUT请求将是幂等的,因为一旦创建了person记录并‘完成’了事务,随后的PUT请求将什么也做不了。但是现在您有了一个不同的问题--要真正更新这个person记录,您需要向另一个URL发出一个PUT请求--一个代表person记录的请求,而不是创建它的事务。因此,现在您有两个单独的URL,您的API客户端必须知道并针对它们发出请求来操作相同的资源。

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

票数 4
EN

Stack Overflow用户

发布于 2010-06-05 01:39:40

我刚看到一个帖子:GUID不唯一的简单证明

虽然这个问题被普遍地嘲笑,但其中一些答案却深入解释了GUID。GUID的大小似乎是2^128,而且随机产生两个相同数目的GUID的几率很低,在所有实际用途中都是不可能的。

也许客户端可以生成自己的事务id,大小相当于GUID,而不是向服务器查询一个。如果有人能让我丢脸,请告诉我。

票数 2
EN

Stack Overflow用户

发布于 2010-06-02 03:42:28

我不确定我对你的问题有一个直接的答案,但我看到了一些可能导致答案的问题。

您的第一个操作是GET,但是它不是一个安全的操作,因为它正在“创建”一个新的事务Id。我认为POST是一个更合适的动词。

您提到您关注的是用户通过两次往返而产生的性能问题。这是因为您的用户将一次创建500个对象,还是因为您所处的网络存在巨大的延迟问题?

如果两次往返不是响应用户请求创建对象的合理开销,那么我建议HTTP不是适合您的场景的协议。但是,如果您的用户需要同时创建大量的对象,那么我们可能会找到一种更好的方法来公开资源来启用该功能。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2954783

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档