我正在开发一个REST API
,我正在尝试理解如何处理分层资源。
背景
让我们从一个简单的例子开始。在我的应用程序接口中,我有Users,User profiles和Reviews。
用户的资源表示应该是:
User: {
"u1": "u1value", // User's attributes
"u2": "u2value",
...
"links": [{
"rel": "profile",
"href": "http://..." // URI of the profile resource
}, {
"rel": "review",
"href": "http://..." // URI of the review resource
}]
}
用户配置文件资源表示形式应为:
UserProfile: {
"p1": "p1value", // Profile attributes
"p2": "p2value",
...
"links": [{
"rel": "owner",
"href": "http://..." // URI of the user resource
}]
}
审阅资源表示形式应为:
Review: {
"r1": "r1value", // Review attributes
"r2": "r2value",
...
"links": [{
"rel": "owner",
"href": "http://..." // URI of the user resource
}]
}
资源URI可以是:
对用户的
http://api.example.com/users/{userid}
:访问权限resourcehttp://api.example.com/users/{userid}/profile
:对用户配置文件的访问权限resourcehttp://api.example.com/users/{userid}/review
:对用户的审查资源的访问权限
创建资源:创建用户的正确方式是什么?
现在我想创建一个新用户:
POST http://api.example.com/users {"u1": "bar", "u2": "foo"}
和我得到了新的userid = 42POST http://api.example.com/users/42/profile {"p1": "baz", "p2": "asd"}
PUT http://api.example.com/users {"u1": "bar", "u2": "foo", links: [{"rel": "profile", "href": "http://api.example.com/users/42/profile"]}
我的担忧是:
发布于 2013-04-02 10:02:33
你的担忧是有根据的,你的问题列表是正确的。如果我可以建议的话,您的方法看起来非常像是使用关系数据库方法并执行插入,从用于下一次插入的序列中检索PK,等等。
让服务器维护参照完整性
作为观察,即使遵循您的原始方案,也完全省略了步骤3。检索用户文档时可见的links
中的URI应由服务器基于配置文件记录的存在而生成。
例如,如果使用关系后端,则选择from USERS以获取用户记录。接下来,从配置文件中选择。如果存在记录,则修改返回数据结构以包括引用。
POST整个文档
解决您提出的其他问题的一种常见方法是允许将整个文档发布到用户URL (如MongoDB等NoSQL数据库)。这里的文档是用户和配置文件:
{
"u1": "bar",
"u2": "foo",
"profile": {
"p1": "baz",
"p2": "asd"
}
}
在此方法中,服务器上的端点接收嵌套结构(文档)并执行INSERT into USERS,检索PK,然后使用此PK执行INSERT into PROFILES。在服务器端执行此操作可以解决以下几个问题:
请注意,这种方法是对上面详细介绍的API的补充-您仍然希望能够直接访问用户的配置文件。
GET - client可以指定字段
与老牌公司的API进行比较是很有趣的。以LinkedIn为例。在他们的developer API中,用户的默认GET只返回用户名、标题和URI。
但是,如果请求指定了其他字段,则可以获取嵌套数据,例如,http://developer.linkedin.com/documents/understanding-field-selectors中的第二个示例返回用户的姓名和他们所担任职位的公司名称列表。您可以为Profiles和Reviews实现类似的方案。
用于更新文档属性的修补程序
有了插入和查询,可能值得考虑如何更新(补丁)数据。覆盖一个字段是很明显的,所以你可以,例如,修补http://api.example.com/users/42如下:
{
"u1": null,
"u2": "new-foo",
"profile": { "p1": "new-baz"}
}
这将取消设置u1
,将u2
设置为new-foo
,并将配置文件的p1
更新为new-baz
。请注意,如果缺少字段(p2
),则不会修改该字段。正如this answer中解释的那样,补丁比旧的PUT更可取。
如果您只需要更新配置文件,请将新的配置文件记录直接修补到http://api.example.com/users/42/profile
删除应级联
最后,删除可以使用DELETE方法来完成,该方法指向您想要删除的资源-可以是User、Profile或Review。实现级联删除,这样删除用户会删除他/她的个人资料和评论。
发布于 2013-04-08 18:49:32
你应该坚持使用HATEOAS,并取消引用你在响应中得到的URL:
为了便于访问,假设User.profile
包含带有rel == profile
的link
的href
。
创建用户
有了你描述的帖子。但是它不应该返回一个id,而应该返回一个用户,以及它的链接。
User: {
"u1": "bar", // User's attributes
"u2": "foo",
...
"profile": "http://api.example.com/users/42/profile",
"links": [{
"rel": "profile",
"href": "http://api.example.com/users/42/profile"
},
...
]
}
在这一点上,User.profile的配置文件资源(可以是http://api.example.com/users/42/profile
,或者您将来迁移到的任何位置)是默认配置文件应该是的任何内容,例如空文档或只填充了所有者链接。
更新配置文件
profile = GET User.profile
profile.p1 = "baz"
profile.p2 = "asd"
PUT profile to the same url you just dereferenced
通过取消引用文档上的href,而不是使用从响应中获得的id来构建urls,当API发生变化时,您的客户端将不必进行更改。就像当-profile被移动到http://profiles.cdn.example.com/时--profile获取一个p3值
“旧的”API客户端将继续工作,而无需更改任何代码。
发布于 2013-04-04 14:56:57
实际上,从成功的步骤(1)开始,您应该得到创建的HTTP代码201和新创建的资源的地址(URL),而不仅仅是ID号。如果第(2)步失败,REST API应该指出问题是否出在客户端,比如格式错误的文档(问题代码4xx)或服务器(5xx)。例如,如果在此期间删除了资源42,则应返回未找到代码404。
这就是无状态REST API的问题所在--它们不能支持由多个请求组成的事务。要实现这一点,您必须在服务器上维护一个会话(状态)。
顺便说一句,示例中步骤(3)中的URL建议您替换所有用户,并且可能应该阅读http://api.example.com/users/42。
您可以选择一次提交完整的user+profile文档,在一个原子事务中将其拆分为两个数据库记录,或者允许持久化部分用户数据,即没有配置文件的用户。
选择取决于上下文。例如,用户没有配置文件(所以它可以由用户提供)可能是完全没有问题的。相反,拥有不属于任何用户的配置文件记录可能是不可接受的。关于实施此逻辑的讨论超出了您的问题的范围,并将根据您选择的持久存储(数据库)的类型而有所不同。关系数据库使用外键执行此操作。
https://stackoverflow.com/questions/13235201
复制相似问题