[英]Transaction safety in RESTful APIs
我想知道如何在RESTful API中建立事务安全性,而RESTful API中的一切都是围绕单个实体构建的。
数据库模型:
用户在浏览器中执行的步骤:
提出的要求:
PATCH
/ PUT
发票数据/订单号。 POST
项目。 DELETE
项目。 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.