我一直在使用POST的REST API来创建对象。 每过一段时间,服务器将创建对象,但它接收到之前的客户端将被断开201 Created
响应。 客户只看到失败的POST请求,并再次尝试后,服务器愉快地创建一个重复的对象...
其他人一定有过这样的问题,对不对? 但我周围的谷歌,每个人都似乎只是忽略它。
我有2个解决方案:
A)使用PUT代替,并创建客户端上(GU)的标识。
B)一个GUID添加到客户机上创建的所有对象,并让服务器执行他们的UNIQUE
-ness。
A不符合现有的框架非常好,和B感觉就像一个黑客攻击。 如何其他人解决这个问题,在现实世界?
编辑:
随着Backbone.js的,你可以在创建客户端上的对象设置一个GUID作为id。 当它被保存,骨干会做一个PUT请求。 让你的REST后端处理PUT到不存在的ID的,而你设定。
Answer 1:
我总是用B钮 - 由于在服务器端的任何问题属于DUP的检测。
Answer 2:
一个已经提出用于此另一种解决方案是POST仅一次(POE) ,其中,所述服务器生成单次使用POST的URI的是,当使用一次以上,将导致服务器返回一个响应405。
的缺点是:1)的POE草案允许在没有标准化任何进一步的进展到期,因而2)实现它需要修改客户端通过服务器来使用新的POE头,以及额外的工作来实现POE语义。
谷歌搜索,你可以找到那些虽然使用它几的API。
另一个想法我对解决这个问题是一个有条件的POST,这是我的描述,并要求反馈的这里 。
人们似乎对防止重复资源创建在情况下,唯一的URI一代是无法在客户端上被投入,因此需要POST的最佳方式没有达成共识。
Answer 3:
重复的检测是组装机,并能获得非常复杂。 真正的不同,但类似的请求,可以在同一时间到达,也许是因为在恢复网络连接。 并重复请求可以到达数小时或数天的一部分,如果网络连接断开了。
所有其他anwsers标识符讨论的是在响应给了一个错误重复请求的目标,但是这通常只是煽动客户端来获取或生成一个新的ID,然后再试一次。
一个简单而强大的方式来解决这个问题如下:服务器应用程序应该存储到不安全的请求的所有响应,那么,如果他们看到了重复的请求,他们可以重复前面的响应和别的什么也不做 。 这样做对所有不安全的请求,将解决一大堆棘手的问题。 “复制”是通过一个应用程序级别ID确定的,无论是客户机生成的GUID或由服务器生成的序列号。 在此第二种情况下,请求 - 响应应专门只是为了交换的ID。 我喜欢这样的解决方案,因为专用的工序,使客户觉得他们得到一些宝贵的东西,他们需要照顾。 如果他们能产生自己的标识,他们更可能把该行的循环,每一个血淋淋的请求将有一个新的ID内。
采用这种方案,所有职位都是空的,而POST仅用于检索的动作标识符。 所有放,删除是完全幂等:连续请求得到相同的(存储和回放)响应,并进一步导致什么情况发生。 关于这个模式的最好的事情是它的功夫(熊猫)的质量。 这需要一个弱点:为客户的倾向重复请求他们得到了意外的响应的任何时间,并把它变成一个力:-)
我有一点谷歌文档在这里 ,如果任何人关心。
Answer 4:
你可以试试两步法。 您请求创建一个对象,它返回一个令牌。 然后在第二个请求,要求使用令牌状态。 直到使用令牌请求的状态,你离开它的“上演”状态。
如果第一次请求之后,客户端断开连接,他们不会有令牌和对象保持“上演”无限期或直到你与另一个进程,删除它。
如果第一个请求成功,你有一个有效的凭证,当你想它没有任何重建,你可以抓住创建的对象多次。
没有理由为什么令牌不能在数据存储对象的ID。 您可以在第一个请求中创建对象。 第二个请求真的只是更新“上演”字段。
Answer 5:
服务器发出的标识符
如果您正在使用的情况下处理它在哪里发出的标识符的服务器,创建一个临时的对象,上演状态。 (这是一个固有的非幂等的操作,所以它应该与POST完成。)然后,客户端必须做进一步的操作就可以将其从阶段状态转移到活跃/保留状态(这可能是一个PUT资源的属性,或合适的POST到资源)。
每个客户端应该能够得到某种方式的阶段状态(可能与其它资源混合)的资源列表,并应该能够删除他们,如果他们还是上演创建的资源。 您也可以定期删除已停用一段时间上演的资源。
你不需要揭示一个客户的上演资源,任何其他客户端; 他们只需要在验证步骤之后,在全球范围存在。
客户端发出的标识符
另一种方法是客户端发出的标识符。 你在哪里造型有点像一个文件存储,作为文件名一般用户代码显著这是很有用的。 在这种情况下,你可以使用PUT做资源的创建,你可以做到这一切idempotently。
这样做的下侧是,客户能够创建的ID,所以你无法控制,在全国各地,他们使用什么标识。
Answer 6:
出现这个问题的另一种变体。 有一个客户端生成一个唯一的ID表明,我们要求客户解决这一问题而生。 我们有一个公开暴露的API和有客户使用这些API整合100S考虑的环境。 实际上,我们在客户端代码和无法控制自己的执行唯一性的正确性。 因此,它很可能是最好是有智力的理解,如果一个请求是重复的。 这里是一个简单的方法是计算和存储基于从用户输入属性每个请求的校验和,定义一些时间阈值(X分钟)和比较,从对过去X分钟收到的那些相同的客户端为每个新请求。 如果校验和匹配,也可能是一个重复的请求,并添加一些挑战机制,为客户解决这个问题。 如果客户正在与X分钟内相同参数的两个不同的要求,这可能是值得保证,这是故意的,即使它有一个独特的请求ID到来。 这种方法可能不适合每个用例,但是,我觉得在那里执行第二通话的商业影响力高,有可能花费一个客户而言,这将是十分有用。 考虑支付处理引擎的情况下的中间层在重试结束一个失败的请求或客户双点击导致提交由客户层两个请求。
Answer 7:
设计
算法[液1]
- REST到达与UUID
- Web服务器检查UUID是在内存中缓存黑名单表(如果是的话,回答409)
- 服务器将请求写入到数据库(如果不是由ETS过滤)
- DB检查的UUID被写入之前反复
- 如果是的话,回答409服务器,黑名单,内存缓存和磁盘
- 如果不重复写入数据库,并回答200
算法[溶液2]
- REST到达与UUID
- 保存在内存缓存表中的UUID(到期30天)
- Web服务器检查UUID是在内存高速缓存黑名单表[返回HTTP 409]
- 服务器将请求写入到数据库[返回HTTP 200]
在溶液2中,仅在存储器中创建以创建内存缓存黑名单阈值,所以DB将永远不会对重复进行检查。 “复制”的定义是“即进入一段时间的任何请求”。 我们还复制磁盘上的内存缓存表,所以我们启动服务器之前填充它。
在解决方案1中,会有永远不会重复的,因为我们总是在写入磁盘的前检查一次,如果它的复制,未来往返将由内存缓存来处理。 该解决方案是大的查询更好,因为要求有没有imdepotents,但它也不太optmized。
对于HTTP POST响应代码时资源已存在
文章来源: Avoid duplicate POSTs with REST