组合密钥资源REST服务

时间:2013-04-23 22:18:57

标签: rest asp.net-web-api composite-key asp.net-web-api-routing attributerouting

我遇到了一个工作问题,我无法找到有关在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部分
  • 获取/api/Products/1/Parts - 获取产品1的所有部件
  • 获取/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部分之间关联的数据,而不是第2部分作为第1部分成员的数据,这通常是基于其他示例的情况,例如我在其他地方看到的/Book/5/Chapter/3
  • POST这里没有任何线索,他们期望IRI看起来像什么。不幸的是,他们是决策者。

在一天结束时,我想我要求的是验证,或指示我可以指出并说“看,这是其他人做的。”

处理由复合键识别的资源的典型做法是什么?

2 个答案:

答案 0 :(得分:18)

我也喜欢/api/Products/1/Parts/2的美学。您也可以将多条路线转到同一操作,这样您就可以加倍并提供/api/Parts/2/Products/1作为同一资源的备用网址。

对于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 :(得分:0)

我建议:

  • POST /api/PartsProductsAssoc:在零件和产品之间创建链接。在POST数据中包含零件和产品ID。
  • 获取,删除,删除/api/PartsProductsAssoc/<assoc_id>:使用<assoc_id>读取/更新/删除链接(不是部分或产品ID,是的,这意味着在您的PartsProductsAssoc表中创建新列)。
  • 获取/api/PartsProductsAssoc/Parts/<part_id>/Products:获取与给定部分关联的产品列表。
  • 获取/api/PartsProductsAssoc/Products/<product_id>/Parts:获取与给定产品相关的零件清单。

采取这种方法的原因:

  • 每个链接的单个完全限定URI。
  • 修改链接会修改单个REST资源。

有关详细信息,请参阅56:30的https://www.youtube.com/watch?v=hdSrT4yjS1g