"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" 答案)。以下是您可能需要考虑的一些要点:
将子帐户资源嵌入客户资源将导致更多数据始终随 /customers/{id} 请求发回。
非嵌入子帐户资源将需要客户端发送多个 HTTP 请求,如果它同时需要基本客户信息和帐户信息。
您需要确定您的安全范例将如何与嵌入式资源一起工作。 (即是否可以允许获取客户信息但不允许查看客户帐户?)
在您的域中拥有一个没有客户的帐户是否有意义?帐户可以转让所有权吗?如果不是,那么 /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
假设我有一个拥有多个帐户的客户。实际上,任何与另一个复杂对象具有“一对多”关系的数据对象都可以。
一个例子可能是:
{ 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" 答案)。以下是您可能需要考虑的一些要点:
将子帐户资源嵌入客户资源将导致更多数据始终随 /customers/{id} 请求发回。
非嵌入子帐户资源将需要客户端发送多个 HTTP 请求,如果它同时需要基本客户信息和帐户信息。
您需要确定您的安全范例将如何与嵌入式资源一起工作。 (即是否可以允许获取客户信息但不允许查看客户帐户?)
在您的域中拥有一个没有客户的帐户是否有意义?帐户可以转让所有权吗?如果不是,那么 /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