繁体   English   中英

RESTful API中的事务安全

[英]Transaction safety in RESTful APIs

我想知道如何在RESTful API中建立事务安全性,而RESTful API中的一切都是围绕单个实体构建的。

数据库模型:

  • 发票
  • 项目

用户在浏览器中执行的步骤:

  1. 更改订单号。
  2. 添加一个项目。
  3. 删除一个项目。
  4. 编辑项目。

提出的要求:

  1. PATCH / PUT发票数据/订单号。
  2. POST项目。
  3. DELETE项目。
  4. PATCH / PUT项目。

问题

如果在上述任何请求之后发生错误,则进一步的调用可能会破坏数据完整性。 此外,先前的请求也必须撤消。 例如,如果删除项目失败,则必须倒退第1步和第2步,以使总发票与以前一样。

可能出现的另一个问题是浏览器崩溃,互联网连接中断,服务器故障或其他原因。

如何确保在某种事务中执行某些操作以维护数据完整性和安全性?

因此,REST要记住的是“状态转移”位。 您不是在告诉服务器更新资源所需的步骤,而是在告诉服务器更新后资源应处于的状态,因为您已经在客户端上对其进行了更新,现在只是将这个新状态转移到服务器上。服务器。

假设您在服务器上有一个发票项目,看起来像服务器上的此JSON

{
    invoice_id: 123,
    invoice_description: "Some invoice",
    invoice_items: [
        {item_id: 10, item_desc: "Large item", order_amount: 34},
        {item_id: 11, item_desc: "Small item", order_amount: 400}
    ]
}

用户希望将该发票作为单个原子交易进行编辑。 首先,他们从服务器获取发票。 这基本上是说“请给我发票的当前状态”

GET /invoices/123

然后,用户可以根据需要编辑该发票。 他们决定大项目的数量应为40而不是34。他们决定完全删除小项目。 最后,他们决定在发票中添加另一个“超小”项目。 用户编辑发票后,客户端具有以下发票

{
    invoice_id: 123,
    invoice_description: "Some invoice",
    invoice_items: [
        {item_id: 10, item_desc: "Large item", order_amount: 40},
        {item_id: 30, item_desc: "Extra small item", order_amount: 5}
    ]
}

因此,客户端具有与服务器不同状态的发票。 现在,用户希望将其发送回服务器以进行存储,因此它会将发票的新状态放入服务器。

PUT /invoices/123

本质上说:“这是该资源的新状态。”

现在,根据希望验证服务器的程度,服务器可以简单地接受发票所处的新状态,也可以对每次更改进行全部验证。 您想做什么多少取决于您。

您至少要检查一下,当用户在其客户端上编辑此发票副本时,没有其他客户端将更新的发票放入服务器。 您可以通过检查HTTP请求的各种标头(例如etag标头http://en.wikipedia.org/wiki/HTTP_ETag )来完成此操作

如果服务器出于任何原因决定此更新无效,则仅使整个PUT请求失败。 这就是使用HTTP进行交易的原因。 一个需求应该工作或失败。 如果失败,则服务器有责任确保资源不受失败请求的影响。 从服务器上的植入角度来看,您可能会对新JSON进行一些验证,然后尝试将新数据保存到DB事务内的数据库中。 如果发生任何故障,则数据库将保持原始状态,并告知用户PUT无法正常工作。

如果请求失败,则应向用户返回HTTP状态代码和响应,以解释PUT要求失败的原因。 这可能是因为在用户考虑其更改时,其他人已编辑了发票。 可能是因为用户试图将发票置于无效状态(例如,用户试图放入没有任何项目的发票,这破坏了公司的业务逻辑)。

当然,您可以开发一个URI方案,该方案允许编辑发票中的各个项目。

GET /invoices/123/items/10

您可以从发票ID 123获得项目ID10。但是,如果这样做,则必须允许彼此独立地编辑这些资源。 如果我通过发送删除命令删除项目10

DELETE /invoice/123/items/10

该动作必须是独立交易。 如果其他请求与此相关,则必须改为如上所述,通过在单个请求中更新发票本身来执行此操作。 您永远都不能通过单个HTTP请求将资源置于无效状态,或以另一种方式将资源置于不需要通过多个HTTP请求才能使资源进入有效状态(因此永远不需要一串HTTP请求)为了有效而工作)

希望能有所帮助

很好的答案,非常感谢。 我只有一点不确定:如果用户发回带有没有ID的新项目的更新发票,因为它总是在服务器上生成的,那该怎么办? AFAIK至少PUT在这里不是正确的,而是POST。 但是,这是怎么做的?

是的,PUT在这里是错误的。 PUT应该是幂等的,这意味着您应该能够对一个资源发出多个PUT请求,如果它们是相同的请求,则所有这些请求的最终结果都应该相同。

如果您再次考虑状态转移,这是有道理的,那么多次执行相同状态的PUT仍应以该状态下的资源结束。 如果我将PNG文件上载到资源20次,则PNG文件应该仍然是相同的PNG文件,就像我刚上传一次一样。

因此,在将PUT放入服务器的状态下,您不应有任何明确的地方。 如果省略了该项目的ID,则实际上是在对服务器说“作为此状态更新的一部分,创建一个项目”。 当然,如果您运行10次,您将创建10个新项目,发票将不会处于相同状态。

因此,在这里进行POST会更好一些,如果您只是在更新项目,则可能需要在“项目”端点进行操作以明确起见。

POST /invoices/123/items

[
    {item_id: 10, item_desc: "Large item", order_amount: 40},
    {item_desc: "Extra small item", order_amount: 5}
]

然后,服务器可以使用响应主体中新创建的ID向您返回发票项目的状态

[
    {item_id: 10, item_desc: "Large item", order_amount: 40},
    {item_id: 30, item_desc: "Extra small item", order_amount: 5}
]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM