REST:返回复杂的嵌套数据与多个调用

我有一个REST API由NodeJS提供给AngularJS前端。

我与users

 GET /api/users #Returns all users POST /api/users #Create new user GET /api/users/:id #Return a user PUT /api/users/:id #Edit a user DELTE /api/users/:id #Delete a user 

这是一个用户:

 { login : "admin" email : "admin@admin.com" pass : "hashedpassword" ... } 

我的用户可以属于groups

 GET /api/users/:id/groups #Return the groups of a user 

他们也可以有constraints或者可以inheritance组的constraints

 GET /api/users/:id/constraints #Return the constraints of a user GET /api/groups/:id/constraints #Return the constraints of a group 

问题 :

我正在做一个pipe理页面,显示所有的用户,他们的组,他们的约束。

我是不是该 :

  1. 在javascript(Angular)前面的for循环中请求很多请求?

就像是 :

 $http.get(/api/users).then(function(result) { result.data.forEach(function(user) { $http.get('/api/users/'+user.data.id+'/groups).then(function(groups) { groups.forEach(function(group) { $http.get('/api/groups/'+group.data.id+'/constraints) }) }) }) }) 
  1. 创build一个path/api/users/withConstraintsAndGroups

这将返回一个所有的用户与他们的组和他们的约束大列表。


我发现解决scheme1是非常好的,可维护的,简单的,通用的,但我担心非常糟糕的性能

我发现解决scheme2是丑陋的,难以维护,难以编码,不是通用的,而是具有良好的性能

我应该select哪种解决scheme?

来吧,多less成本的性能,根据论文,结果将几乎相同,多个请求与一个大规模的请求。 我们在2016年,除非你处理互联网连接不好,你应该通过多次请求来做到这一点。

您正在为全球99%的人口开发应用程序,或者使用Opera的1%部分开发您的应用程序(用于增强function)?

另外,如果我们正在谈论devise一个REST API,那么一致性可能就是这种API的主要思想。 在你的情况下,如果你写第二个方法,你必须在代码的其余部分写一些类似的东西来保持所有控制器的一致性,这是不可能的。

Also, an API is an API and should not be modified when a front end application is being built on that API. 当你有10个应用程序正在请求你的API的情况下考虑,但是你处于与你呈现的相同的状态,但是你需要对每个应用程序有不同的响应。 你要添加新的方法到API? 这是一个不好的做法

REST api中的路由应根据您拥有的逻辑资源(可使用HTTP动词处理的对象)进行:

例子:

  * when you have a relation between entities /users/3/accounts // should return a list of accounts from user 3 /users/3/accounts/2 // should return account 2 from user 3 * custom actions on your logical resources /users/3/approve /accounts/2/disable 

也是一个好的API应该能够提供partial requests (例如添加一些querystrings参数到一个通常的请求: users/3?fields=Name,FirstName ), versioningdocumentation (apiDocs.js在这种情况下是非常有用的)请求types(json), paginationtoken authcompression (我从来没有做过最后一个:))

你的问题基本归结为:

哪一个更好,一个大的HTTP请求,还是很多小的?

要记住的一件事是客户端和服务器之间的预期networking延迟(ping时间)。 在带宽很高的高延迟情况下,许多小的请求将比一个小的请求得多。

此外,有一个大的请求可以使压缩响应的效率更高,并避免额外的HTTP请求和响应头的开销。

说了这么多,我仍然build议你选项1 开始 ,仅仅因为你说你编码很容易。 然后看看它是否符合你的要求。 如果不是,则尝试选项2

作为最后一点,我通常更喜欢对很多小请求提出大的请求,因为这样可以很容易地将每个请求定义为一个完全应用或回滚的事务。 这样,我的客户端就不会进入这样的状态:其中一些小的请求成功了,而另外一些则失败了,现在我的API提供的数据的本地副本不一致。

我build议修改您的端点服务用户资源,以接受(通过查询参数)哪些相关资源应作为respose的一部分包括在内:

 /api/users?includes=groups&includes=constraints 

这是一个非常灵活的解决scheme,如果您的需求在未来扩展,您可以轻松支持更多的关联资源。

如果你关心如何构build响应,我build议看看JSON:API 。

使用JSON:API一个优点是,如果多个用户共享相同的组/约束,那么您将不必获取同一个实体的多个表示。

你在问

哪个更好…

根据SO规则,这是不可接受的,所以我会假设你的问题在于REST在某些情况下应该做什么。

REST是基于资源的 ,它们本身就像拥有自己的数据和“访问者/获取者”的对象。

当你要求/api/users/:id/groups ,你告诉你要从users资源/表中访问一个特定的用户和一个id并从那个用户那里访问他的groups列表/她属于(或拥有,或任何你想要的解释)。 在任何情况下,查询对于这个资源都是非常具体的(在REST中,这个资源是唯一的,因为每个资源都可以通过它的URL通用定向),而不应该被“碰撞”或者被误解为另一个资源。 如果有多个对象可以作为URL的目标(例如,如果只调用/api/groups ),那么你的资源就是一个数组。

考虑到这一点,我会考虑(并build议)总是返回与您指定的用例相匹配的最大select。 例如,如果我要创build该API,我可能会这样做:

/users所有用户的/users列表

/users/:id特定的用户

/users/:id/groups特定用户已join的/users/:id/groups

/users/groups列表中至less有一个用户的所有组

/users/groups/:id来自/users/groups的特定组的/users/groups/:id描述(详细信息)

等等…

每个用户案例应该完全由URL定义。 如果没有,那么你的REST规范可能 (我几乎可以肯定)是错误的。

==========

所以,回答你的问题 ,考虑到这个描述,我会说你的答案是:这取决于你需要做什么。

如果你想在主页面显示每一个约束(可能,但不是太优雅/废话UI),那么是的,第一种方法是好的(希望你的最终用户不会杀了你)。

如果您只想要求资源(例如,在popup鼠标hover的信息时或在进入pipe理站点的特定部分之后),那么您的解决scheme就像第二个(是的,这是一个大的…只有当你真的需要这个资源时,你应该使用它)。

你有没有考虑path/api/users/:id/constraints的需求?

顺便说一句, withConstraintsAndGroups不是一个REST资源。 REST资源是名词( constraintsgroups ,但不是两者)。 不是动词或形容词。

在我看来,pipe理页面的性能是一个太大的问题。 唯一的区别是,在#1你有3个API调用,#2只有一个。 数据应该是一样的,数据应该是合理的,不是非常大的。 所以,如果#1更容易编码和维护,你应该去。 你不应该有任何性能问题。