如何以RESTful的方式表示/维护主-细节关系
本文关键字:维护 细节 关系 表示 RESTful 方式 | 更新日期: 2023-09-27 17:58:08
在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来允许我们更改Form所属的组?
到目前为止,我已经提出了两种潜在的方法,每种方法都有利弊:
将关系表示为链接结构:
{
"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中工作的。我还想要一个可以通用地应用于模型中任何导航属性的解决方案。
明信片上的答案。。。这两种方法中的任何一种有效吗?或者还有其他我错过的方法吗?
Pete
基于数据模型构建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响应中包含超媒体链接,我建议您查看超文本应用程序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
请注意,评论确实具有标识,但仅在博客文章的上下文中。
在你的情况下(根据我们在推特上的讨论),你说Forms
确实有FormGroup
之外的身份。你还说你可以有孤儿表格——那些不属于一个群体的表格。
考虑到这一点,通过Form
资源管理FormGroup
关系是有意义的。最简单的方法是在对表单资源执行POST或PUT时,只设置/更新FormGroupId
属性。如果(同样,这取决于您的情况),您需要在不更新Form
资源上任何其他属性的情况下更改FormGroup
,您可能希望发送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
不幸的是,没有"正确的方式"来表示资源之间的关系。我的建议是选择感觉最自然的。
你说得对。目前ASP上的资源世界。NET Web API是一个混合的API,您可以在某个地方定义路由,而您必须在其他地方管理资源关系和链接。您可以通过定义每个路由来进行分层路由,这对维护和性能都有影响。
我正在考虑把这些结合起来。注意空间:)
现在,就回答你的问题而言,我会这样做:
/api/formgroups/1
/api/formgroups/1/forms/123
目前,维护链路和设置路线仍或多或少是临时性的。