REST中的Client Server API模式(不可靠的网络用例)

时间:2011-02-15 22:06:25

标签: ruby-on-rails api networking rest

假设我们在不可靠的网络(数据包丢弃)上发生了客户端/服务器交互。客户端正在调用服务器的RESTful api(通过http over tcp):

  • http://server.com/products
  • 发布POST
  • 服务器正在创建“产品”资源的对象(将其持久保存到数据库等)
  • 服务器正在返回201使用“http://server.com/products/12345
  • 的位置标头创建
  • !包含http响应的TCP数据包被丢弃,最终导致tcp连接重置

我看到以下问题:客户端永远不会获得新创建的资源的ID,但服务器将创建资源。

问题:这个应用程序级别的行为或框架应该处理这个问题吗? Web框架(特别是Rails)应该如何处理这种情况?关于此主题,是否有关于REST的文章/白皮书?

5 个答案:

答案 0 :(得分:5)

当服务器没有响应POST时,客户端将收到错误。然后,客户端通常会重新发出请求,因为他们认为该请求已失败。我能想到解决这个问题的两种方法。

一个是客户端可以生成某种请求标识符,例如guid,它包含在请求中。如果服务器收到带有重复GUID的POST请求,则它可以拒绝它。

另一种方法是PUT而不是POST来创建。如果您无法让客户端生成URI,那么您可以要求服务器提供带有GET的新URI,然后对该URI执行PUT。

如果您搜索“make POST idempotent”之类的内容,您可能会找到一些关于如何执行此操作的其他建议。

答案 1 :(得分:4)

如果创建重复资源是不合理的(例如,具有相同标题,描述等的产品),则可以在服务器上生成唯一标识符,可以针对创建的资源跟踪该唯一标识符以防止重复请求被处理。与在客户端上生成唯一ID的Darrel's suggestion不同,这也会阻止单独的用户创建重复的资源(您可能找到或未找到所需的资源)。客户将能够通过响应代码区分“已创建”响应和“重​​复”响应(分别在下面的示例中为201和303)。

用于生成此类标识符的伪代码 - 在这种情况下,是请求的规范表示的哈希:

func product_POST
    // the canonical representation need not contain every field in
    // the request, just those which contribute to its "identity"
    tags = join sorted request.tags
    canonical = join [request.name, request.maker, tags, request.desc]
    id = hash canonical

    if id in products
        http303 products[id]
    else
        products[id] = create_product_from request
        http201 products[id]
    end
end

此ID可能是也可能不是创建资源的URI的一部分。就个人而言,我倾向于单独跟踪它们 - 以额外的查找表为代价 - 如果URI将暴露给用户,因为哈希往往是丑陋的,人类难以记住。

在许多情况下,在一段时间后“过期”这些独特的哈希值也是有意义的。例如,如果您要进行汇款API,则用户将相同数量的钱转移到相隔几分钟的同一个人可能表示客户从未收到过“成功”回复。如果用户每月向同一个人转移相同数量的钱,另一方面,他们可能正在支付他们的租金。 ; - )

答案 2 :(得分:1)

您描述的问题归结为避免所谓的双重添加。正如其他人所说,你需要让你的帖子具有幂等性。

这可以在框架级别轻松实现。该框架可以保留已完成响应的缓存。请求必须具有请求唯一,以便将任何重试视为此类,而不是新请求。

如果成功的响应在去往客户端的途中丢失,客户端将使用相同的请求进行重试,服务器将以其缓存的响应进行响应。

你留下了缓存的持久性,保持响应的时间等等。一种方法是在给定的一段时间后从服务器缓存中删除响应,这将取决于你的应用程序域和流量,可以留下作为框架部分的可配置步骤。另一种方法是强制客户端发送确认。可以将ack作为单独的请求发送(请注意,这些请求也可能丢失),或者作为真实请求的额外数据。

虽然我的建议与其他人的建议相似,但我强烈建议您保持这层网络弹性,只处理掉落请求/响应,不允许它处理来自单独请求的重复资源,这是应用程序级别任务。合并这两个部分将会影响所有功能,并且不会让您明确分离责任。

这不是一个简单的问题,但是如果你保持清洁,你可以让你的应用程序对不良网络更具弹性,而不会引入太多复杂性。

对于其他人的相关经验,请here

祝你好运。

答案 3 :(得分:0)

正如其他响应者所指出的,这里的基本问题是标准的HTTP POST方法与其他方法一样不是幂等的。正在努力为称为Post-Once-Exactly或POE的幂等POST方法建立标准。

现在我并不是说这对于你所描述的情况下的每个人来说都是一个完美的解决方案,但如果是你在写服务器和客户端的情况,你可能能够利用一些想法来自POE。草案在这里:http://tools.ietf.org/html/draft-nottingham-http-poe-00

这不是一个完美的解决方案,这可能是自提交草案以来六年内没有真正起飞的原因。这里讨论了一些问题和一些聪明的备选方案: http://tech.groups.yahoo.com/group/rest-discuss/message/7646

答案 4 :(得分:-1)

HTTP是无状态协议,这意味着服务器无法打开HTTP连接。所有连接都由客户端初始化。所以你无法在服务器端解决这样的错误。

我能想到的唯一解决方案:如果您知道哪个客户创建了产品,您可以为其提供所创建的产品,如果它会提取该信息。如果客户再也没有与您联系,您将无法传输有关新产品的信息。