【问题标题】:REST API design: one endpoint with if/else logic or two separate role based endpointsREST API 设计:一个带有 if/else 逻辑的端点或两个独立的基于角色的端点
【发布时间】:2019-11-05 14:10:28
【问题描述】:

我有一个 API 设计/版本控制难题。 假设我有一个端点/api/customers,它获取所有客户(忽略分页)。但是有一个转折点:如果一个普通的user 访问这个端点,他们只会得到该用户创建的客户,而没有其他人(我可以检查访问令牌和子字段来确定谁发送了请求)。其他用例:如果admin 访问此端点,他们应该获得所有客户,无论是谁获得的。

现在我的问题是从 API 设计的角度来看:在 API 控制器本身内进行if/else 角色检查是否更好,以确定我是返回所有(管理员)客户还是特定(用户)客户,或者我应该区分在用户和管理员的端点之间? IE。所有客户的管理员唯一端点是/api/admin/customers,普通用户仍然可以访问他们的/api/customers

【问题讨论】:

  • 后者,如指定一个仅限管理员的端点,在我看来是要走的路
  • @binjamin 感谢您的评论,您介意告诉我您的理由吗?
  • 您使用什么语言来构建您的 API?
  • Java 和 Spring,但这个问题应该并且与语言/框架无关。
  • 当您希望保护管理资源免受“用户”访问时,您不会从拥有两个资源中获得任何收益,只需将“if”拆分为两个不同的方法。另一方面,拥有一个单独的“我的用户”端点使管理员可以将他们创建的用户与“所有”用户分开查看。

标签: rest api design-patterns hateoas


【解决方案1】:

在 REST 中,具有共享相同表示的多个资源是正常的。

例如,学术论文的“作者的首选版本”是其值随时间变化的映射,而“在 X 会议论文集中发表的论文”的映射是静态的。这是两个不同的资源,即使它们在某个时间点都映射到相同的值。区分是必要的,以便可以独立识别和引用这两种资源。软件工程中的一个类似示例是在引用“最新修订版”、“修订版号 1.2.7”或“Orange 版本中包含的修订版”时单独标识受版本控制的源代码文件。 -- Fielding, 2000

这与这种方法完全一致,您可能有一个资源用于“所有用户”,而另一个资源用于“由 Bob 创建的用户”。

如果你想使用 same 资源标识符来提供不同的表示,事情就会变得曲折。也就是说,当 Alice 查看“我创建的用户”时,她看到的是“Alice 创建的用户”,而当 Bob 查看“我创建的用户”时,他看到的是“Bob 创建的用户”。

一种可能性是让“我创建的用户”重定向到适当的资源。当目标资源不在本地缓存中时,它适用于允许额外往返的“工作”值。

在 HTTP/2 中,服务器推送可能会为您省去一些往返的痛苦。

shared caches 的规则应该可以防止您将 Alice 对“我”资源的视图发送给 Bob,反之亦然,但是了解各种标头的含义是很有用的,这样您就不会不经意间禁用该保护。

在某些“读取您自己的写入”设置中,拥有不同的资源可能是个问题,因为缓存不会知道不安全的请求已使两个资源无效。 Bob 通过 POST 向“我创建的用户”创建一个新用户,并且相应的缓存条目无效……但“所有用户”是不同的缓存键,不会失效。因此,如果 Bob 查看所有用户视图,他可能会看到以前缓存的副本,而没有他刚刚在自己的视图中看到的更改。

在某些情况下,考虑子资源是有意义的。

/api/customers
/api/customers#created-by-Alice
/api/customers#created-by-Bob

但是,如果您试图减少正在交换的不相关数据的数量,那就不太合适了。

【讨论】:

    【解决方案2】:

    应该是同一个端点。否则,调用您的 API 的每个前端必须具有相同的逻辑来确定角色和端点映射。

    【讨论】:

    • 您尝试给出正确答案,但请尝试解释。这会很有帮助。最好有一个 API 端点,并且应该从后端处理角色基础逻辑。
    • 谢谢阿维。我是 stackoverflow 的新手。
    【解决方案3】:

    这取决于你的项目。

    1. 如果只有你提到的 2 个案例
      • 仅获取该用户为regular 用户创建的客户
      • admin 用户获取所有客户

    那么,最好使用 1 个端点,通过添加中间件来检查当前用户角色。

    1. 如果您计划扩展您的项目。 例如如果还需要admin 用户来获取该用户创建的客户,则最好创建 2 个端点。一个用于所有客户,另一个用于当前用户的客户。喜欢 - api/customers/all, api/customers/me

    【讨论】:

      【解决方案4】:

      我认为 /api/customers 对于提到的情况很好。这类似于对 index.html 的网页请求,将不同的内容返回给不同的用户。

      如果你想扩展它(例如 Alice 请求 Bob 的列表),你可以支持可选的查询参数:

      /api/customers?accessibleTo=bob
      /api/customers?createdBy=bob
      

      这可能需要进行授权检查(Alice 是否有权访问 Bob 的列表?),在未授权时返回 403(或 404,取决于您的理念)。

      也不要忘记缓存。避免不同用户对同一 URL (/api/customers) 的两次请求会导致一个用户获取另一个用户的列表。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-15
        • 1970-01-01
        • 2014-03-17
        相关资源
        最近更新 更多