组合键资源 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)

一个产品可以有很多部分,而一个部分可以是很多产品的组成部分。关联表还包含与关联本身相关的额外信息,而不是需要编辑。

我们有 ProductsControllerPartsController 类,它们使用定义为 controller/id/action 的路由模板处理常见的 GET/PUT/POST/DELETE 操作,以便以下 IRI 工作:

GET,POST /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 添加其他方法来管理关联数据:

GET,PUT,DELETE /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 服务的主要内容,如果未能解决你的问题,请参考以下文章

Win+Break”组合键 怎么打?

PostMessage如何发送Ctrl组合键

捕获组合键 键盘组合键

是否可以从非 REST 部分形式组合 REST 和非 REST 操作和资源?

emacs 的使用:哪些是真正有用的组合键

vc中应该如何向窗口发送组合键消息