RESTful幂等 [英] RESTful idempotence

查看:88
本文介绍了RESTful幂等的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm designing a RESTful web service utilizing ROA(Resource oriented architecture).

我正在尝试一种有效的方法,以确保在服务器指定资源密钥的情况下,对于创建新资源的PUT请求具有幂等性.

I'm trying to work out an efficient way to guarantee idempotence for PUT requests that create new resources in cases that the server designates the resource key.

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

From my understanding, the traditional approach is to create a type of transaction resource such as /CREATE_PERSON. The the client-server interaction for creating a new person resource would be in two parts:

步骤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)

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

The problem that I see with this approach is that it requires sending two requests to the server in order to do to single operation of creating a new PERSON resource. This creates a performance issues increasing the chance that the user will be waiting around for the client to complete their request.

我一直在尝试消除用于消除第一步的想法,例如在每个请求中预先发送事务ID,但是我的大多数想法还有其他问题,或者涉及牺牲应用程序的无状态性.

I've been trying to hash out ideas for eliminating the first step such as pre-sending transaction-id's with each request, but most of my ideas have other issues or involve sacrificing the statelessness of the application.

有没有办法做到这一点?

Is there a way to do this?

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

The solution that we ended up going with was for the client to acquire a UUID and send it along with the request. A UUID is a very large number occupying the space of 16 bytes (2^128). Contrary to what someone with a programming mind might intuitively think, it is accepted practice to randomly generate a UUID and assume that it is a unique value. This is because the number of possible values is so large that the odds of generating two of the same number randomly are low enough to be virtually impossible.

一个警告是我们让客户从服务器(GET uuid/)请求一个UUID.这是因为我们不能保证客户端运行的环境.如果出现问题,例如在客户端上植入随机数生成器,那么很可能会发生UUID冲突.

One caveat is that we are having our clients request a UUID from the server (GET uuid/). This is because we cannot guarantee the environment that our client is running in. If there was a problem such as with seeding the random number generator on the client, then there very well could be a UUID collision.

推荐答案

您在创建操作中使用了错误的HTTP动词. RFC 2616 指定POSTPUT的操作的语义.

You are using the wrong HTTP verb for your create operation. RFC 2616 specifies the semantic of the operations for POST and PUT.

第9.5段:

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

POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line

第9.6段

PUT方法要求 封闭的实体应存储在 提供的Request-URI.

PUT method requests that the enclosed entity be stored under the supplied Request-URI.

该行为有一些细微的细节,例如PUT可用于在指定的URL创建新资源(如果尚不存在).但是,POST永远不要将新实体放在请求URL上,而PUT应该总是将任何新实体放在请求URL上.与请求URL的这种关系将POST定义为CREATE,将PUT定义为UPDATE.

There are subtle details of that behavior, for example PUT can be used to create new resource at the specified URL, if one does not already exist. However, POST should never put the new entity at the request URL and PUT should always put any new entity at the request URL. This relationship to the request URL defines POST as CREATE and PUT as UPDATE.

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

As per that semantic, if you want to use PUT to create a new person, it should be created in /CREATE_PERSON/{transaction_id}. In other words, the transaction ID returned by your first request should be the person key used to fetch that record later. You shouldn't make PUT request to a URL that is not going to be the final location of that record.

但是,更好的是,您可以通过使用POST/CREATE_PERSON作为原子操作来执行此操作.这样一来,您只需一个请求即可创建新的人记录,并在响应中获得新的ID(也应在HTTP Location标头中引用该ID).

Better yet, though, you can do this as an atomic operation by using a POST to /CREATE_PERSON. This allows you with a single request to create the new person record and in the response to get the new ID (which should also be referred in the HTTP Location header as well).

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

Meanwhile, the REST guidelines specify that verbs should not be part of the resource URL. Thus, the URL to create new person should be the same as the location to get the list of all persons - /PERSONS (I prefer the plural form :-)).

因此,您的REST API变为:

Thus, your REST API becomes:

  • 得到所有人-GET /PERSONS
  • 结交单身人士-GET /PERSONS/{id}
  • 创建新人员-POST /PERSONS,其正文包含新记录的数据
  • 更新现有人员或创建具有知名ID-PUT /PERSONS/{id}的新人员,其正文包含更新记录的数据.
  • 删除现有人员-DELETE /PERSONS/{id}
  • to get all persons - GET /PERSONS
  • to get single person - GET /PERSONS/{id}
  • to create new person - POST /PERSONS with the body containing the data for the new record
  • to update existing person or create new person with well-known id - PUT /PERSONS/{id} with the body containing the data for the updated record.
  • to delete existing person - DELETE /PERSONS/{id}

注意:出于两个原因,我个人更喜欢不使用PUT创建记录,除非我需要创建一个子记录,该子记录的ID与来自不同数据集的现有记录的ID相同(也称为穷人的外国人")键":-)).

Note: I personally prefer not using PUT for creating records for two reasons, unless I need to create a sub record that has the same id as an already existing record from a different data set (also known as 'the poor man's foreign key' :-)).

更新:您很正确,POST不是幂等的,并且符合HTTP规范. POST始终返回一个新资源.在上面的示例中,新资源将是事务上下文.

Update: You are right that POST is not idempotent and that is as per HTTP spec. POST will always return a new resource. In your example above that new resource will be the transaction context.

但是,我的意思是,您希望使用PUT来创建新资源(人员记录),并且根据HTTP规范,新资源本身应位于URL处.特别是,您的方法遇到的问题是,与PUT一起使用的URL是POST创建的事务性上下文的表示,而不是新资源本身的表示.换句话说,人员记录是更新交易记录的副作用,而不是它的直接结果(更新的交易记录).

However, my point is that you want the PUT to be used to create a new resource (a person record) and according to the HTTP spec, that new resource itself should be located at the URL. In particular, where your approach breaks is that the URL you use with the PUT is a representation of the transactional context that was created by the POST, not a representation of the new resource itself. In other words, the person record is a side effect of updating the transaction record, not the immediate result of it (the updated transaction record).

当然,使用这种方法,PUT请求将是幂等的,因为一旦创建了个人记录并且交易已完成",则后续的PUT请求将不执行任何操作.但是现在您遇到了另一个问题-要真正更新该个人记录,您将需要对另一个URL发出PUT请求-一个代表个人记录而不是创建该记录的事务的请求.因此,现在您有两个单独的URL,您的API客户端必须知道这些URL,并针对它们发出请求以操纵同一资源.

Of course, with this approach the PUT request will be idempotent, since once the person record is created and the transaction is 'finalized', subsequent PUT requests will do nothing. But now you have a different problem - to actually update that person record, you will need to make a PUT request to a different URL - one that represents the person record, not the transaction in which it was created. So now you have two separate URLs your API clients have to know and make requests against to manipulate the same resource.

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

Or you could have a complete representation of the last resource state copied in the transaction record as well and have person record updates go through the transaction URL for updates as well. But at this point, the transaction URL is for intends and purposes the person record, which means it was created by the POST request in first place.

这篇关于RESTful幂等的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆