我想知道如何在RESTful API中建立事务安全性,其中所有内容都是围绕单个实体构建的。
数据库模型:
浏览器中用户执行的步骤:
要求:
PATCH
/ PUT
发票数据/订单号。POST
item。DELETE
item。PATCH
/ PUT
item。如果在上述任何请求发生错误后,进一步调用可能会破坏数据完整性。此外,之前的请求必须撤消。例如。如果删除该项目失败,则必须重绕步骤1和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}
]