【问题标题】:Should I have multiple views/endpoints of a resource in a RESTful service?我应该在 RESTful 服务中有多个资源视图/端点吗?
【发布时间】:2014-01-21 10:53:33
【问题描述】:

假设我正在创建一个 RESTful 服务来通过 Web 处理我的仓库的订单。

  • 我想允许客户创建帐户
  • 我希望客户管理员能够为其办公室中的其他用户创建帐户
  • 我想允许客户用户创建订单
  • 我希望网站管理员能够创建和管理所有客户帐户
  • 我希望网站管理员能够创建和管理所有用户
  • 我希望网站管理员能够创建和管理所有订单

鉴于这些要求。我最初的想法是以这种方式设计端点。

# to request a new customer account
/customers/request {POST}
# create and view customers - limited to admins
/customers {GET, POST}
# view customer info, update a customer
/customers/{customer_id} {GET, PATCH}
# create and view orders for a customer
/customers/{customer_id}/orders {GET, POST}
# view and update order for a customer
/customers/{customer_id}/orders/{order_id} {GET, PATCH}

我非常有信心,这些路径是有意义的,并且遵循一般的宁静想法。但是,我不确定如何处理用户端点。问题是,我希望客户管理员能够创建可以使用其客户帐户创建订单的用户。客户管理员在哪里发布来完成此操作?我有几个想法。 跟着这个answer,我想到了这个。

# creation of users always done through this endpoint no matter what the
# authenticated user's role is
/users { GET, POST }
# associate user with customer
/customers/{customer_id}/user_memberships { GET, POST }

这种方法的问题是客户帐户的管理员如何获取用户的 ID 以与客户帐户关联。 /users 上的任何 GET 请求都将通过仅检索属于其客户帐户的用户来过滤。但是,由于用户是在成员资格之前创建的,因此他们永远无法查看该用户。 我也想只有两个端点来创建用户。

# create a user for a customer account
/customers/{customer_id}/users {GET, POST}
# root users endpoint only accessible to admins
/users {GET, POST}
# return same user
/users/1
/customers/{customer_id}/users/1

它本质上归结为使用客户 url 前缀作为授权手段。让两个端点使另一个无效似乎有点奇怪。如果根端点只是子资源端点的视图呢?

# view all users in system - admin only
/users {GET}
# create & view admin users
/admin/users {GET, POST}
# create internal office users
/locations/{location_id}/users { GET, POST }
# create customer users
/customers/{customer_id}/users { GET, POST }

在这种情况下,我们仍然可以在子资源上缓存 GET 响应,因为除非子资源的特定 id 上存在 POST 或 PATCH/DELETE,否则它们不会更改。

这种风格似乎也适用于订单。管理员可以查看所有订单,即使它们在技术上属于客户。

# admin can view all orders
/orders?customer_id=1234
/orders

我有点喜欢根资源是子资源视图的想法,允许基于 url 更轻松地进行授权。

所以,我想毕竟,我真正的问题是:

即使其中一个只是子资源的聚合视图并且不允许通过该端点创建资源,具有表示同一资源的多个端点是否存在问题?

【问题讨论】:

  • 据我了解,例如。您希望对于特定的customerId,您希望该客户仅查看其用户而不能创建只能由管理员创建的用户,因此也可以使用spring security来完成,这肯定会造成问题,所以您有根据您的要求对客户进行分类。

标签: rest authorization entity-relationship


【解决方案1】:

据我了解,例如。您希望对于特定的customerId,您希望该客户仅查看其用户而不能创建只能由管理员创建的用户,因此也可以使用spring security来完成,这肯定会造成问题,所以您有根据您的要求对客户进行分类。

【讨论】:

  • 考虑服务的 3 个角色:管理员、客户管理员和客户用户。管理员可以创建和查看任何内容。客户管理员可以创建其他用户来访问客户帐户。客户用户可以代表客户帐户做事。例如A 公司的所有者想要访问该系统。他们发布给客户/请求,获取客户帐户并成为客户帐户的管理员用户。然后,他可以通过发布到 /customers/customer_id/users 为员工 X 创建一个帐户,他可以通过发布到 /customer/customer_id/orders 来创建订单。
  • @chrislbs 是的,我的朋友,你可以在 spring security 的帮助下通过向特定成员提供特定 URL 的访问来做到这一点,你也可以对这些应用约束。
  • @chrislbs 这是你想要的吗,然后请点击我的回答,这样也对其他人有帮助
  • 您的评论没有解决我的问题。我知道我将如何使用我的 URL 方案进行授权。我的问题是,是否有多种访问资源的方式是不好的宁静设计。我添加了授权组件来阐明一个潜在的用例。
  • @chrislbs 我的朋友,这不会影响你的 restful 设计你可以将多个 URI 映射到单个资源,rest 架构非常迅速地支持这种设计,但这种设计会降低你的代码性能。
【解决方案2】:

您不应将 API 的设计、REST 原则和授权需求混为一谈。您应该以某种方式设计您的 API:

  • 易于使用
  • 易于维护
  • 简单易懂

API 设计的 RESTful 方法试图解决这些不同的问题。 RESTful 方法是关于识别您拥有的对象、它们的状态以及它们可能的转换。

这就是它停止的地方。现在,您想知道授权。您希望能够根据用户是谁(管理员、客户...)和目标资源是什么(客户记录...)来控制用户可以对给定记录执行的操作。

您需要做的是在您的 REST API 之上以松散耦合的方式部署一个授权框架。换句话说,您想要外部化授权。您绝对不想将授权直接构建到您的 API 中。想象一下,突然你有了新的授权规则/约束:你必须重新编码你的 API。这样做你会破坏所有的客户。这会导致糟糕的用户体验。

因此,我们确定您需要将授权外部化。伟大的。有哪些不同的方法可以做到这一点?这取决于您使用的语言和框架。

你可以使用:

  • Java 中的 Spring 安全性
  • PHP 中的 Yii
  • Ruby 中的CanCanCan
  • ...还有更多

您还可以实现自己的过滤器,例如在您的 REST 端点前面的 Java 中的 Servlet 过滤器。

最后,您可以转向基于 XACML 的成熟的基于属性的授权模型。有几种开源和供应商替代方案。如果您不熟悉基于属性的访问控制或 XACML,请查看以下链接:

使用 XACML,您可以集中定义策略,例如:

  • 管理员可以查看所有客户帐户
  • 管理员可以修改分配给他/她的客户帐户
  • 客户只能查看和编辑自己的帐户

然后在授权服务(在称为策略决策点的 XACML 中)中评估策略。授权服务公开了一个二进制授权 API,您的 API 可以调用该 API:用户 Alice 可以查看记录 foo 吗?

使用基于策略的外部化授权和 XACML,您可以在业务逻辑(您的业务 API)和授权逻辑之间实现松耦合,从而更容易维护和更新。

【讨论】:

  • 我明白您关于授权与 API 设计分离的观点。但是,我并没有真正尝试为授权而设计 API。我的主要问题是:您不能拥有不属于客户的订单(客户/客户 ID/订单)。但是,我还希望能够查询所有订单(/orders)。我最关心的是有两个端点可能引用相同的资源。 (/orders?cust_id=1234) === (/customers/1234/orders)。你认为这是一个问题吗?
  • 我不认为这是一个问题。如果你仔细想想,这就像说你有一个 API 来管理订单和一个 API 来管理客户。碰巧的是,您可以从订单中确定客户记录,就像您可以从客户记录中确定订单记录一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-12
  • 2020-11-09
相关资源
最近更新 更多