如何以RESTful方式表示\维护主细节关系?

时间:2013-02-06 19:41:34

标签: c# entity-framework rest asp.net-web-api

在我们的ASP.net Web API背后,我们有一个代码第一实体框架模型。考虑以下(简化)实体:

public class Form
{
    public long Id { get; set; }

    public string Identifier { get; set; }

    public virtual FormGroup Group { get; set; }
}

public class FormGroup
{
    public long Id { get; set; }

    public string Identifier { get; set; }

    public virtual ICollection<Form> Forms { get; set; }
}

正如您所看到的,FormGroups和Forms之间存在1-Many关系。我的问题是,如何设计一个允许我们更改表单所属的组的RESTful API?

到目前为止,我已经提出了两种可能的方法,每种方法都有利弊:

将关系表示为链接结构:

{
    "Id" : 1,
    "Identifier" : "Form1",
    "link" : {
        "rel": "http://myapi/res/formgroup",
        "href": "http://myapi/formgroups/1"
    }
}

优点:

  • 包含表单组表示的有意义链接

缺点:

  • 此类链接没有特别标准
  • 服务器端逻辑变得更加复杂

使用PUT将资源链接在一起

data: {
    "Id" : 1,
    "Identifier" : "Form1"
}

PUT data to http://myapi/formgroups/1/forms

优点:

  • 表示中没有无关的属性

缺点:

  • 资源可以通过多个URI识别,即/ forms / 1和/ formgroups / 1 / forms / 1
  • 发送除形成密钥所需数据之外的任何数据要么无关紧要,要么令人困惑

我对这些方法中的任何一种都不是特别满意,因为它们似乎都有比正面更多的缺点。

我知道一个'正确'的解决方案是实现DTO并从我的资源表示中分离我的实体模型,这样我就可以添加一个我可以设置的FormGroupId。

与所有优秀的程序员一样,我很想知道这是如何在RESTful API中工作的。我还想要一个解决方案,我可以一般地应用于我的模型中的任何导航属性。

明信片上的答案......这些方法中的任何一种是否有效,或者是否存在我错过的其他方式?

皮特

1 个答案:

答案 0 :(得分:2)

基于数据模型构建API只会限制您的整体设计。我倾向于从我的陈述中回复

通过完全控制您的陈述,您可以表达您想要的关系。例如,您可以决定在FormGroup代表中包含Form的代表:

GET /api/forms/1

    {
      "Id" : 1,
      "Name" : "Form 1",
      "FormGroup" : {
        "Id" : 1,
        "Name" : "Form Group 1"
      }
    }

或在FormGroup表示中包含表格:

GET /api/formgroups/1

    {
      "Id" : 1,
      "Name" : "Form Group 1",
      "Forms" : [
        {
          "Id" : 1,
          "Name" : "Form 1"
        },
        {
          "Id" : 2,
          "Name" : "Form 2"
        }
      ]
    }

事实上,你可以做到这两点。只需确保每个资源只有一个URI标识符。

如果你真的想拥抱REST并在API响应中包含超媒体链接,我建议你查看Hypertext Application Lanaguage(HAL)。 HAL提供了表达资源之间相关链接的约定。例如:

{
  "_links" : {
    "self" : { "href" : "/api/forms/1" },
    "related" : { "href" : "/api/formgroups/1" }
  }
  "Id" : 1,
  "Name" : "Form 1",
  "FormGroupId" : 1
}

希望您能看到代表资源的方式并不能决定您如何管理他们之间的关系。

如何做到这一点应该在很大程度上取决于您的场景中的逻辑,并且通常可以通过决定拥有关系来帮助您。

采用典型的Blog Post .* Comments示例。评论本身没什么意义。如果我要设计一个用于管理博客评论的API,我可能会这样做:

POST   /posts/1/comments  - Add a comment to post 1
DELETE /posts/1/comments/1 - Remove comment 1 from post 1

请注意,评论确实具有标识,但仅限于博客文章的上下文中。

在你的情况下(基于我们在twitter上的讨论)你说Forms确实在FormGroup之外有身份。你还说你可以拥有孤儿形式 - 那些不属于一个团体的形式。

考虑到这一点,通过FormGroup资源管理Form关系是有意义的。最简单的方法是在对表单资源执行POST或PUT时设置/更新FormGroupId属性。如果(这取决于您的方案),您需要能够更改FormGroup而不更新您可能希望发送PATCH的Form资源上的任何其他属性或创建“子资源”管理关系:

POST /api/forms/1/group -- set the form group for forms/1
{
  "formGroupId" : 10
}

DELETE /api/forms/1/group -- unlink forms/1 from its group

不幸的是,没有“正确的方法”来表示资源之间的关系。我的建议是选择最自然的方式。