REST API 设计:将多个 POST 或 PUT 调用组合在一起?

问题描述 投票:0回答:1

我有一个具有通常结构的 REST API

/customers                 // get a list of all customers
/customers/22              // get customer with the ID 22
/customers/22/orders       // get all orders for customer 22
/customers/22/orders/156   // get order 156 for customer 22

我有简单的客户和订单 DTO。订单不是客户 DTO 的一部分。每次检索 Customer 对象时,您不一定需要所有客户订单的列表。因此,如果您确实想要客户对象及其所有报告的列表,您可以对 API 进行两次调用。

但是 Customer 对象也有几个非常简单的子对象的集合,例如电话号码、地址等。我已将这些部分作为客户 DTO 的一部分,并且我尚未提供子记录的端点。

例如,

GET /customers/22
可能会让你

{
  "id": 22,
  "forename": "Jack",
  "surname": "Smith",
  "telephones": [
    {
      "id": 45,
      "type": "mobile",
      "number": "[phone number here]"
    },
    {
      "id": 46,
      "type": "home",
      "number": "[phone number here]"
    }
  ],
  "emails": [
    {
      "id": 226,
      "type": "personal",
      "number": "[address here]"
    },
    {
      "id": 242,
      "type": "work",
      "number": "[address here]"
    }
  ],
  "addresses": [
    {
      "id": 35,
      "type": "home",
      "house": "24",
      "street": "Hyacinth Avenue",
      "town": "London"
    },
    {
      "id": 7,
      "type": "work",
      "house": "54",
      "street": "Hydrangea Road",
      "town": "Nottingham"
    }
  ]
}

所有子对象都有自己的唯一标识符。

我对处理客户记录更新的方式并不完全满意。我接受

POST /customers/22

在正文中,我期望完整的 Customer 对象,包括所有子记录。服务器计算出哪些(如果有)子记录已被创建、更新或删除,并对数据库进行适当的更改。

这有几个问题。

  1. 如果您只想更改主客户记录上的某些内容(例如姓氏的拼写),则必须将所有子记录作为更新请求的一部分提交,这很麻烦。

  2. 如果您提交了以下内容

POST /customers/22

body:

{
  "id": 22,
  "forename": "Jack",
  "surname": "Smyth"
}

然后,除了更新姓氏之外,API 还会将其解释为您想要删除所有电话号码、电子邮件地址和地址。 API 文档中已经明确说明了这一点,但这感觉有点像一场即将发生的灾难。

我认为最好以与任何其他子资源完全相同的方式处理子对象,并为每个子对象提供单独的 URI,例如

/customers
/customers/22
/customers/22/telephones
/customers/22/emails
/customers/22/addresses

并接受通常的 GET、POST 和 PUT。

因此,如果您想要“完整”的客户记录,则必须对 API 进行四次调用。这并不理想,但如果它能让 API 更清晰并且不易发生事故,我就同意了。

另一个含义是,如果最终用户同时更改客户端记录中的多个内容,则客户端有责任确定需要执行哪些插入、更新和删除操作,而不是让 API 工作这一切都为你准备好了。不过我对此很满意。

更改设计的最后一个问题是,目前,如果用户要更新姓氏、添加电话号码、删除电子邮件地址以及更新街道地址,所有这些都将通过一次发送到 API调用,并且在数据库上,所有更新都将在同一事务中完成。我们使用 SQL Server 系统版本表来跟踪客户记录的所有更改。查看客户记录更改历史的能力是该系统的一个非常重要的功能。我们称之为客户时间表。

如果我要更改为新设计,他们将看到一个接一个的四个较小的事件,而不是客户时间线中的一个事件。从最终用户的角度来看,他们可能已经一起进行了所有这些更改,因此他们希望看到包含所有更改的单个时间线事件。

由于 SQL Server 系统版本表的工作方式,我无法手动设置记录上的“ValidFrom”值。如果我希望它们都具有相同的 ValidFrom 日期,那么它们 have 都位于同一个数据库事务中。

我也许可以稍后再处理,例如当用户查询客户的时间线时,我将彼此相隔几秒内的所有事件分组在一起,但这感觉有点模糊。

是否有一种通用方法允许客户端将多个 API 调用捆绑到一个调用中,以表明它们都应该作为同一事务的一部分进行处理?

rest asp.net-web-api api-design
1个回答
0
投票

您可以创建一个 BATCH 端点。但也有一些缺点

简而言之:

优点:

确保多个操作之间的原子性和一致性。

让服务器在一次调用中处理插入、更新和删除,避免多次往返。

由于所有操作都捆绑在一起,因此简化了客户端逻辑。

缺点:

增加了 API 的复杂性。

需要仔细记录,因为每个操作都需要明确的验证和错误处理。

那看起来怎么样?

你这样称呼它:

POST /customers/22/batch

您的请求正文可以如下所示

{
  "operations": [
    { "action": "update_customer", "data": { "surname": "Doe" } },
    { "action": "add_telephone", "data": { "type": "mobile", "number": "0815" } },
    { "action": "delete_email", "id": 123},
    { "action": "update_address", "id": 1, "data": { "street": "Some street" } }
  ]
}

服务器将确保所有更改都得到处理或根本不处理

© www.soinside.com 2019 - 2024. All rights reserved.