REST API 设计:一个具有 if/else 逻辑的端点或两个独立的基于角色的端点
REST API design: one endpoint with if/else logic or two separate role based endpoints
我有一个 API design/versioning 难题。
假设我有一个端点 /api/customers
可以获取所有客户(忽略分页)。不过有一个转折:如果常规 user
访问此端点,他们将只会获得该用户创建的客户,而不会获得其他人(我可以检查访问令牌和子字段以确定是谁发送了请求)。其他用例:如果 admin
访问此端点,他们应该获得所有客户,无论他们是谁获得的。
现在我的问题是从 API 设计的角度出发:在 API 控制器本身中进行 if/else
角色检查以确定我 return 是否更好?所有(管理员)客户或特定(用户)客户,还是我应该区分用户和管理员的端点? IE。所有客户的仅管理员端点将是 /api/admin/customers
,普通用户仍然可以访问他们的 /api/customers
?
应该是同一个端点。否则,调用您的 API 的每个前端必须具有相同的逻辑来确定角色和端点映射。
在 REST 中,多个资源共享相同的表示是正常的。
For example, the "authors' preferred version" of an academic paper is a mapping whose value changes over time, whereas a mapping to "the paper published in the proceedings of conference X" is static. These are two distinct resources, even if they both map to the same value at some point in time. The distinction is necessary so that both resources can be identified and referenced independently. A similar example from software engineering is the separate identification of a version-controlled source code file when referring to the "latest revision", "revision number 1.2.7", or "revision included with the Orange release." -- Fielding, 2000
您可能拥有一个资源用于“所有用户”,另一个资源用于“Bob 创建的用户”,这与该方法完全一致。
事情变得复杂的地方是您想要使用相同 资源标识符来提供不同的表示形式。也就是说,当 Alice 查看“我创建的用户”时,她看到的是“Alice 创建的用户”,而当 Bob 查看“我创建的用户”时,他看到的是“Bob 创建的用户”。
一种可能性是将“我创建的用户”重定向到适当的资源。当目标资源不在本地缓存中时,它的工作原理是允许额外往返的“works”值。
在 HTTP/2 中,服务器推送可能会让您免去一些往返的痛苦。
shared caches 的规则应该保护您不将 Alice 对“我”资源的看法发送给 Bob,反之亦然,但了解各种 headers 这样您就不会无意中禁用该保护。
在某些“读取您自己的写入”设置中,拥有不同的资源可能是一个问题,因为缓存不会知道不安全的请求已使 两个 资源无效。 Bob 通过 POST 为“我创建的用户”创建了一个新用户,并且相应的缓存条目无效......但是“所有用户”是一个不同的缓存键,并且不会失效。因此,如果 Bob 查看所有用户视图,他可能会看到以前缓存的副本,而没有他刚刚在自己的视图中看到的更改。
在某些情况下,考虑 sub-resources 是有意义的。
/api/customers
/api/customers#created-by-Alice
/api/customers#created-by-Bob
但是,如果您正试图减少交换的不相关数据量,那么这就不太合适了。
这取决于你的项目。
- 如果只有你说的两种情况
- 只获取该用户为
regular
用户创建的客户
- 获取
admin
位用户的所有客户
那么,最好通过添加中间件来使用 1 个端点来检查当前用户角色。
- 如果您打算扩展您的项目。
例如如果
admin
用户还需要获取该用户创建的客户,最好创建 2 个端点。一个用于所有客户,另一个用于当前用户的客户。喜欢 - api/customers/all
、api/customers/me
我认为 /api/customers 适合上述情况。它类似于 index.html 向不同用户返回不同内容的网页请求。
如果你想扩展它(例如 Alice 请求 Bob 的列表),你可以支持可选的查询参数:
/api/customers?accessibleTo=bob
/api/customers?createdBy=bob
这可能需要授权检查(Alice 是否有权访问 Bob 的列表?),在未授权时返回 403(或 404,取决于您的理念)。
另外不要忘记缓存。避免不同用户对同一个 URL (/api/customers) 的两次请求导致一个用户获得另一个用户的列表的可能性。
我有一个 API design/versioning 难题。
假设我有一个端点 /api/customers
可以获取所有客户(忽略分页)。不过有一个转折:如果常规 user
访问此端点,他们将只会获得该用户创建的客户,而不会获得其他人(我可以检查访问令牌和子字段以确定是谁发送了请求)。其他用例:如果 admin
访问此端点,他们应该获得所有客户,无论他们是谁获得的。
现在我的问题是从 API 设计的角度出发:在 API 控制器本身中进行 if/else
角色检查以确定我 return 是否更好?所有(管理员)客户或特定(用户)客户,还是我应该区分用户和管理员的端点? IE。所有客户的仅管理员端点将是 /api/admin/customers
,普通用户仍然可以访问他们的 /api/customers
?
应该是同一个端点。否则,调用您的 API 的每个前端必须具有相同的逻辑来确定角色和端点映射。
在 REST 中,多个资源共享相同的表示是正常的。
For example, the "authors' preferred version" of an academic paper is a mapping whose value changes over time, whereas a mapping to "the paper published in the proceedings of conference X" is static. These are two distinct resources, even if they both map to the same value at some point in time. The distinction is necessary so that both resources can be identified and referenced independently. A similar example from software engineering is the separate identification of a version-controlled source code file when referring to the "latest revision", "revision number 1.2.7", or "revision included with the Orange release." -- Fielding, 2000
您可能拥有一个资源用于“所有用户”,另一个资源用于“Bob 创建的用户”,这与该方法完全一致。
事情变得复杂的地方是您想要使用相同 资源标识符来提供不同的表示形式。也就是说,当 Alice 查看“我创建的用户”时,她看到的是“Alice 创建的用户”,而当 Bob 查看“我创建的用户”时,他看到的是“Bob 创建的用户”。
一种可能性是将“我创建的用户”重定向到适当的资源。当目标资源不在本地缓存中时,它的工作原理是允许额外往返的“works”值。
在 HTTP/2 中,服务器推送可能会让您免去一些往返的痛苦。
shared caches 的规则应该保护您不将 Alice 对“我”资源的看法发送给 Bob,反之亦然,但了解各种 headers 这样您就不会无意中禁用该保护。
在某些“读取您自己的写入”设置中,拥有不同的资源可能是一个问题,因为缓存不会知道不安全的请求已使 两个 资源无效。 Bob 通过 POST 为“我创建的用户”创建了一个新用户,并且相应的缓存条目无效......但是“所有用户”是一个不同的缓存键,并且不会失效。因此,如果 Bob 查看所有用户视图,他可能会看到以前缓存的副本,而没有他刚刚在自己的视图中看到的更改。
在某些情况下,考虑 sub-resources 是有意义的。
/api/customers
/api/customers#created-by-Alice
/api/customers#created-by-Bob
但是,如果您正试图减少交换的不相关数据量,那么这就不太合适了。
这取决于你的项目。
- 如果只有你说的两种情况
- 只获取该用户为
regular
用户创建的客户 - 获取
admin
位用户的所有客户
- 只获取该用户为
那么,最好通过添加中间件来使用 1 个端点来检查当前用户角色。
- 如果您打算扩展您的项目。
例如如果
admin
用户还需要获取该用户创建的客户,最好创建 2 个端点。一个用于所有客户,另一个用于当前用户的客户。喜欢 -api/customers/all
、api/customers/me
我认为 /api/customers 适合上述情况。它类似于 index.html 向不同用户返回不同内容的网页请求。
如果你想扩展它(例如 Alice 请求 Bob 的列表),你可以支持可选的查询参数:
/api/customers?accessibleTo=bob
/api/customers?createdBy=bob
这可能需要授权检查(Alice 是否有权访问 Bob 的列表?),在未授权时返回 403(或 404,取决于您的理念)。
另外不要忘记缓存。避免不同用户对同一个 URL (/api/customers) 的两次请求导致一个用户获得另一个用户的列表的可能性。