【问题标题】:How to represent\maintain a master detail relationship in a RESTful way?如何以 RESTful 方式表示\维护主从关系?
【发布时间】:2013-02-06 19:41:34
【问题描述】:

在我们的 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 之间存在一对多的关系。我的问题是,应该如何设计一个允许我们更改表单所属组的 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 中工作的。我还想要一个可以通用地应用于我的模型中的任何导航属性的解决方案。

明信片上的答案...这些方法中的任何一种是否有效,或者是否有其他方法可以做我错过的事情?

皮特

【问题讨论】:

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


    【解决方案1】:

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

    通过完全控制您的陈述,您可以随心所欲地表达关系。例如,您可能决定在您的Form 表示中包含您的FormGroup 表示:

    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 为例。评论本身没有什么意义。如果我要设计一个用于管理博客 cmets 的 API,我可能会这样做:

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

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

    在您的情况下(基于我们在推特上的讨论),您说Forms 确实在FormGroup 之外具有身份。您还说您可以拥有孤儿表格 - 那些不属于某个组的表格。

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

    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
    

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

    【讨论】:

    • 感谢您的见解...我将其标记为答案,因为正如您所说,现在很明显,没有正确的答案。也许我们可以用一个好的解决方案开创某种先例:)
    • 这是不久前的事了,但它仍然会出现在 google 上,所以我将添加一点:如果你想进一步拥抱 REST 并想要链接,你应该看看 @ 987654322@(同时它甚至被包含在 VS-Controllers 中)所以你需要做的编码要少得多。
    【解决方案2】:

    你是对的。目前,ASP.NET Web API 上的资源世界是混合的,您可以在某处定义路由,而您必须在其他地方管理资源关系和链接。您可以通过单独定义每个路由来进行分层路由,这对维护和性能有影响。

    我正在考虑将这些放在一起。看空间:)


    现在就回答您的问题而言,我会这样做:

    /api/formgroups/1
    /api/formgroups/1/forms/123
    

    目前,维护链接和设置路由仍或多或少是临时性的。

    【讨论】:

    • 如果您使用: /api/formgroups/1/forms/123 ,那么您的控制器会是什么样子?就像你在另一个里面有一个控制器? api/{controller}/{id}/{controller}/{id}...我不知道如何创建那种链接,你能给我一些建议/链接吗?提前致谢
    猜你喜欢
    • 2011-11-04
    • 2014-03-12
    • 1970-01-01
    • 2020-07-30
    • 1970-01-01
    • 2018-07-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-17
    相关资源
    最近更新 更多