组合键资源 REST 服务
Posted
技术标签:
【中文标题】组合键资源 REST 服务【英文标题】:composite key resource REST service 【发布时间】:2013-04-17 07:19:18 【问题描述】:我在工作中遇到了一个问题,我找不到有关在 RESTful Web 服务中针对主键是其他资源 ID 组合的资源执行 CRUD 操作的常用标准或实践的信息。我们正在使用 MVC WebApi 创建控制器。例如,我们有三个表:
Product
: PK=ProductId
Part
: PK=PartId
ProductPartAssoc
: PK=(ProductId, PartId)
一个产品可以有很多部分,而一个部分可以是很多产品的组成部分。关联表还包含与关联本身相关的额外信息,而不是需要编辑。
我们有 ProductsController
和 PartsController
类,它们使用定义为 controller/id/action
的路由模板处理常见的 GET/PUT/POST/DELETE 操作,以便以下 IRI 工作:
/api/Products
- 返回所有产品,创建新产品
GET,PUT,DELETE /api/Products/1
- 检索/更新/删除产品 1
GET,POST /api/Parts
- 返回所有部分,创建一个新部分
GET、PUT、DELETE /api/Parts/2
- 检索/更新/删除第 2 部分
GET /api/Products/1/Parts
- 获取产品 1 的所有部件
GET /api/Parts/2/Products
- 获取第 2 部分是组件的所有产品
我遇到的问题是如何为 ProductPartAssoc 资源定义路由模板。获取关联数据的路由模板和 IRI 应该是什么样的? 遵守惯例,我希望是这样的:
GET,POST/api/ProductPartAssoc
- 返回所有关联,创建关联
GET,PUT,DELETE /api/ProductPartAssoc/[1,2]
- 检索/更新/删除产品 1 和第 2 部分之间的关联
我的同事发现这在美学上令人不快,并且似乎认为完全没有ProductPartAssocController
类会更好,而是向ProductsController
添加其他方法来管理关联数据:
/api/Products/1/Parts/2
- 获取产品 1 和部件 2 之间关联的数据,而不是作为部件 1 成员的部件 2 的数据,这通常是基于其他示例(例如 @987654339)的情况@ 我在其他地方看到的。
POST 这里不知道他们期望 IRI 是什么样子。不幸的是,他们是决策者。
归根结底,我想我正在寻求的不是验证,就是我可以指出并说“看,这就是其他人所做的”的方向。
处理由复合键标识的资源的典型做法是什么?
【问题讨论】:
对于链接实体,您可以像 OData 协议为 Odata 服务指定的那样对 uri 空间进行建模。我不建议实施 OData 服务,但值得一看,因为它提供了有用的见解并且更接近您的问题。在此处查看管理实体之间的链接:odata.org/documentation/odata-v2-documentation/operations/… ProductPartAssoc 是什么样的?而且,它支持哪些 CRUD 操作? 这个例子是任意的,但 ProductPartAssoc 类可能看起来像(抱歉简洁 - 此处空间有限): class ProductPartAssoc int ProductId; int PartId;使用量;十进制汇编成本; int 安装程序 ID; 并且需要支持所有四个 CRUD 操作 - 创建、检索、更新、删除。 【参考方案1】:我也喜欢/api/Products/1/Parts/2
的美学。您还可以让多个路由执行相同的操作,因此您可以加倍并提供/api/Parts/2/Products/1
作为同一资源的备用 URL。
对于 POST,您已经知道复合键。那么为什么不消除对 POST 的需求,只使用 PUT 来创建和更新呢?如果您的系统生成主键,则 POST 到集合资源 URL 非常好,但如果您有已知主键的组合,为什么需要 POST?
也就是说,我也喜欢使用单独的 ProductPartAssocController
来包含这些 URL 的操作的想法。您必须进行自定义路由映射,但如果您使用 AttributeRouting.NET 之类的东西,这很容易做到。
例如,我们这样做是为了管理角色中的用户:
PUT, GET, DELETE /api/users/1/roles/2
PUT, GET, DELETE /api/roles/2/users/1
6 个 URL,但只有 3 个操作,都在 GrantsController
中(我们将用户和角色之间的动名词称为“Grant”)。类最终看起来像这样,使用AttributeRouting.NET:
[RoutePrefix("api")]
[Authorize(Roles = RoleName.RoleGrantors)]
public class GrantsController : ApiController
[PUT("users/userId/roles/roleId", ActionPrecedence = 1)]
[PUT("roles/roleId/users/userId", ActionPrecedence = 2)]
public HttpResponseMessage PutInRole(int userId, int roleId)
...
[DELETE("users/userId/roles/roleId", ActionPrecedence = 1)]
[DELETE("roles/roleId/users/userId", ActionPrecedence = 2)]
public HttpResponseMessage DeleteFromRole(int userId, int roleId)
...
...etc
这对我来说似乎是一种相当直观的方法。将动作保存在单独的控制器中也有助于更精简的控制器。
【讨论】:
感谢您的洞察 :-) 两件事:(1)感谢 AttributeRouting.Net 的提示 - 很棒的东西。 (2) 回想起来,您不需要 POST 操作是正确的。感谢您指出了这一点。我将您的回答标记为回答我的问题...【参考方案2】:我建议:
POST/api/PartsProductsAssoc
:在零件和产品之间创建链接。在 POST 数据中包含部件和产品 ID。
GET、PUT、DELETE /api/PartsProductsAssoc/<assoc_id>
:读取/更新/删除与 <assoc_id>
的链接(不是零件或产品 ID,是的,这意味着在您的 PartsProductsAssoc 表中创建一个新列)。
GET /api/PartsProductsAssoc/Parts/<part_id>/Products
:获取与给定部件关联的产品列表。
GET /api/PartsProductsAssoc/Products/<product_id>/Parts
:获取与给定产品相关的部件列表。
采用这种方法的原因:
每个链接都有一个完全限定的 URI。 修改链接会修改单个 REST 资源。如需更多信息,请在 56:30 联系 https://www.youtube.com/watch?v=hdSrT4yjS1g。
【讨论】:
以上是关于组合键资源 REST 服务的主要内容,如果未能解决你的问题,请参考以下文章