"related" 资源的 REST 设计

REST design for "related" resources

假设我有一个拥有多个帐户的客户。实际上,任何与另一个复杂对象具有“一对多”关系的数据对象都可以。

一个例子可能是:

{ id: 1, name: "Bob", accounts: [ { id: 2, name: "Work account" }, { id: 3, name: "Home account" } }

我的问题是,什么时候 appropriate/better 将 帐户 公开为客户的子资源,而不是单独的资源?或者两者兼而有之?

例如,我的第一直觉是:/customers/1 return 上面的对象。如果您想修改其中一个帐户,则必须 POST/accounts/2

另一种方法(我在一些 API 中看到过)是公开另一条路由 /customers/1/accounts 将 return 上面的数组,然后设置POST/PATCH 那里的路由允许 API 用户改变帐户数组。

我对该方法的问题是,如果帐户数组实际上是 "included by reference",则不清楚该 REST 路由是在修改帐户,还是在修改帐户仅修改客户与帐户之间的链接

这里有最佳实践吗?

这是个好问题,有待讨论(没有 "correct" 答案)。以下是您可能需要考虑的一些要点:

  1. 将子帐户资源嵌入客户资源将导致更多数据始终随 /customers/{id} 请求发回。

  2. 非嵌入子帐户资源将需要客户端发送多个 HTTP 请求,如果它同时需要基本客户信息和帐户信息。

  3. 您需要确定您的安全范例将如何与嵌入式资源一起工作。 (即是否可以允许获取客户信息但不允许查看客户帐户?)

  4. 在您的域中拥有一个没有客户的帐户是否有意义?帐户可以转让所有权吗?如果不是,那么 /customers/{id}/accounts/{acct_id} 更有意义。

在 REST 的定义中暗示,在 URI 上发布 HTTP 方法是在修改由 URI 标识的资源,因此默认情况下,您总是在修改帐户,而不是客户和帐户之间的链接。

如果您需要修改帐户链接的功能,您可以发明一种资源,例如 "account link request"(POST /accounts/{id}/linkreqeust 或类似性质的资源)。此请求本身可以有一个状态,您将拥有后端功能来评估请求,然后确定是否应该链接或分离帐户 to/from 客户,然后执行 attach/detach 进程。

总而言之,REST 中没有任何内容可以阻止您使用不同的链接(/accounts/{id};/customers/{id}/accounts/{acct_id})引用相同的资源。我的偏好是,如果拥有子资源没有安全隐患,则将其与端点结合使用以自行访问子资源。如果帐户可以绑定到多个用户(或没有客户),我也会公开 /accounts/{id} 端点。

ex. /customers/1:

{
  "id": "1",
  "name": "John Doe",
  "accounts": {
    "collection": [
      {
        "id": "1",
        "type": "savings",
        "balance": "0",
        "links": {
          "self": "/customers/1/accounts/1"
        }
      },
      {
        "id": "2",
        "type": "checking",
        "balance": "0",
        "links": {
          "self": "/customers/1/accounts/2",
          "detach" : "/customers/1/accounts/2/linkrequest?action=detach"
        }
      }
    ],
    "links": {
      "self": "/customers/1/accounts",
      "attach": "customers/1/accounts/linkrequest?action=attach"
    }
  },
  "links": {
    "self": "/customers/1"
  }
}

正如您已经怀疑的那样,这一切都归结为数据库中的数据表示模型。无论是拥有关系(实际实体列表)、无主关系(您称为 "included by reference" 的关系),甚至是通过反向链接引用(帐户上的客户反向链接字段)的隐式 1:N 关系。

为了通过 GET 检索数据,可以根据表示模型使用以下内容:

 /customers/1/accounts
 /accounts?customer eq 1