RESTful API中的事务安全性

时间:2014-11-27 14:45:23

标签: rest asp.net-web-api

我想知道如何在RESTful API中建立事务安全性,其中所有内容都是围绕单个实体构建的。

实施例

数据库模型:

  • 发票
  • 项目

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

  1. 更改订单号。
  2. 添加项目。
  3. 删除项目。
  4. 编辑项目。
  5. 要求:

    1. PATCH / PUT发票数据/订单号。
    2. POST item。
    3. DELETE item。
    4. PATCH / PUT item。
    5. 问题

      如果在上述任何请求发生错误后,进一步调用可能会破坏数据完整性。此外,之前的请求必须撤消。例如。如果删除该项目失败,则必须重绕步骤1和2,以使整个发票成为之前的状态。

      可能出现的另一个问题是浏览器崩溃,网络连接死亡,服务器故障等等。

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

2 个答案:

答案 0 :(得分:4)

所以用REST记住的是“状态转移”位。您没有告诉服务器更新资源所需的步骤,您告诉服务器更新后资源应该处于的状态,因为您已经在客户端上更新了它,现在只是将这个新状态转移到服务器上。服务器

所以说你在服务器上有一个Invoice项,它在服务器上看起来像这个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

基本上说“这是这个资源的新状态。”

现在,根据您希望验证的程度,服务器可以简单地接受发票所在的新状态,或者可以对每次更改进行一整套验证。你想做多少取决于你。

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

来执行此操作

如果服务器因任何原因决定此更新无效,则只会失败整个PUT请求。这就是为您提供HTTP事务的原因。要求应该工作或失败。如果失败,服务器有责任确保资源未被失败的请求影响。从服务器上的植入角度来看,您可能会对新JSON进行一些验证,然后尝试将新数据保存到数据库事务中的数据库中。如果有任何失败,那么数据库将保持原始状态,并告知用户PUT不起作用。

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

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

GET /invoices/123/items/10

会从发票ID 123中为您提供项目ID 10.但是,如果您这样做,则必须允许彼此独立地编辑这些资源。如果我通过发送删除命令删除项目10

DELETE /invoice/123/items/10

该操作必须是独立的交易。如果其他请求依赖于此,则必须通过在单个请求中更新发票本身来执行此操作,如上所述。您永远不能通过单个HTTP请求将资源置于无效状态,或者换句话说它应该永远不需要多个HTTP请求来使资源进入有效状态(因此永远不需要一串HTTP请求)工作以使其有效)

希望有所帮助

答案 1 :(得分:1)

  

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

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

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

所以你不应该在你投入服务器的状态中有任何明确的含义。如果您遗漏了您实际上对服务器说的项目的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}
]