服务器返回状态200但客户端没有收到它,因为网络连接中断

时间:2018-05-24 09:36:31

标签: rest http post

我有REST服务和客户端(Android应用程序),它向POST服务发送POST请求。在客户端,有需要与Web服务器同步的文档(订单)。同步意味着客户端为每个订单向REST服务发送POST请求。当REST服务收到POST请求时,它会将数据写入数据库并将状态为200的响应发送到客户端。客户收到200并将该订单标记为已同步。

问题是在服务器发送状态200响应之后但在客户端收到响应之前连接断开。客户端不会将订单标记为已同步。下次客户端再次发送此订单,服务器再次在数据库中写入,因此我们有两次相同的订单。

处理这类问题的好习惯是什么?

4 个答案:

答案 0 :(得分:0)

当然,您的文档必须具有唯一标识符。语义上正确的方法是使用您发送该标识符的If-None-Match标题。

然后服务器检查具有该标识符的文档是否已经存在,如果是这种情况,将以412 Precondition Failed进行响应。

答案 1 :(得分:0)

可能的选择之一是在服务器端进行验证。订单应该有一些唯一性参数:name或id或其他东西。但是这个参数也应该由客户端发送。然后你得到这个值(e.x.如果name是唯一的并且客户端发送它),在数据库中找到这个顺序。如果订单已成立,则您无需将其保存到数据库中,并应向客户端发送409 Conflict响应。如果你在数据库中找不到这样的订单,那么你保存它并发送201 Ok响应。

最佳做法:

  • 201 Ok for POST
  • 409冲突 - 如果资源已存在

答案 2 :(得分:0)

您的请求应为idempotent 根据您的描述,您应该使用PUT而不是POST 客户端生成ID(guid)和Upsert逻辑服务器端,帮助实现这一点 这样,您可以为失败的请求实现重试逻辑客户端,而不会引入多个记录。

答案 3 :(得分:0)

  

问题是在服务器发送状态200响应之后但在客户端收到响应之前连接断开。客户端不会将订单标记为已同步。下次客户端再次发送此订单,服务器再次在数据库中写入,因此我们有两次相同的订单。

欢迎来到不可靠消息的世界。

  

处理这类问题的好习惯是什么?

您应该在Marc de Graauw(2010)之前查看Nobody Needs Reliable Messaging。

可靠消息传递的基石是idempotent请求处理。幂等语义以这种方式描述

  

请求方法被视为"幂等"如果使用该方法对服务器的多个相同请求的预期效果与单个此类请求的效果相同。

然而,简单地使用请求方法,并不能为您提供任何帮助。首先,消息中的其他语义可能与幂等请求方法不一致,其次服务器需要知道如何按预期实现效果

幂等请求处理有两种基本模式。更简单的是set,意思是"用我提供的那个覆盖当前的表示"。

// X == 6

server.setX(7)
// X == 7

server.setX(7) <- a second, identical request, but the _effect_ is the same.
// X == 7

替代方案是test and set(有时称为compare and swap);在这种模式中,请求有两个部分 - 确定某个条件的谓词,以及条件确实存在时应用的更改。

// X == 6

server.testAndSetX(6,7)
// X == 7 

server.testAndSetX(6,7) <- this is a no op, because 7 != 6
// X == 7

这是核心理念。

根据您的描述,您正在做的是操纵一系列订单 那里有同样的基本想法。如果您可以根据请求中的信息计算唯一标识符,则可以将集合视为集/键值存储。

// collection.get(Id.of(7)) == Nothing

collection.put(Id.of(7), 7)
// collection.get(Id.of(7)) == Just(7)

collection.put(Id.of(7), 7) <- a second, identical request, but the _effect_ is the same.
// collection.get(Id.of(7)) == Just(7)

如果那不是一个选项,那么你需要一些收集的属性,这些属性会在编辑时改变,并编码到请求中

if (collection.size() == 3) {
   collection.append(7)
}

管理此类内容的通用方法是考虑版本号 - 每次进行更改时,版本号都会作为同一事务的一部分递增

// begin transaction
if (resource.version.get() == expectedVersion) {
   resource.version.set(1 + expectedVersion)
   resource.applyChange(request)
}
// end transaction

对于一个真实世界的例子,考虑JSON补丁,它包含一个test operation,可用作防止&#34;并发&#34;的条件。修改文件。

我们在所有这些test and set方案中描述的是conditional request

的概念
  

条件请求是HTTP请求[RFC7231],其中包含一个或多个标头字段,指示在将方法语义应用于目标资源之前要测试的前提条件。

条件请求规范为您提供了一种泛型方式来描述请求和响应的元数据中的条件,以便泛型 http组件可以有用地提供。

请注意:这有效得到了我们并不能保证服务器能够满足客户的需求。相反,它是一个较弱的:客户端可以安全地重复请求,直到它收到服务器的确认。