c# - 如何以 RESTful 方式表示/维护主要细节关系?

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

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

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
  • 发送除构成 key 所需的数据以外的任何数据都是无关紧要或令人困惑的

我对这两种方法都不是特别满意,因为它们的缺点似乎都多于优点。

我知道“正确”的解决方案是实现 DTO 并将我的实体模型与我的资源表示分开,这将允许我添加一个我可以设置的 FormGroupId。

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

明信片上的答案...这些方法是否有效,或者是否有另一种我错过的方法?

皮特

最佳答案

基于数据模型构建 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 提供了一种表达资源之间相关链接的约定。例如:

{
  "_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 之外具有身份。您还说过您可以有孤儿形式 - 那些不属于某个组的形式。

考虑到这一点,您可以通过 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

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

关于c# - 如何以 RESTful 方式表示/维护主要细节关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14737393/

相关文章:

c# - Entity Framework 中的 Linq 嵌套投影

c# - 重定向时授权 header 丢失

c# - 如何从 Pacer.gov API 发出案例请求?

c# - 使用 Skype API 从调用 ID 获取聊天 ID

c# - Entity Framework 和MySql更新实体

c# - Entity Framework 为导航属性添加新记录

javascript - 如何进行本地文件下载

c# - 在winforms中多次运行 "FFMPEG"

c# - 如何将 char @"\"转换为 Escape String\by C#

c# - 通过 WCF 服务返回 MySqlDataAdapter 列表导致错误