如何在RESTful API中处理对象层次结构?


72

我目前正在为现有的PHP应用程序设计API,为此,我正在研究REST作为一种明智的体系结构方法。

我相信我对关键概念有一个合理的了解,但是我正在努力寻找能够解决对象层次结构和REST的人。

这是问题所在...

在[应用程序]业务对象层次结构中,我们有:

Users 
 L which have one-to-many Channel objects
 L which have one-to-many Member objects

在应用程序本身中,我们使用延迟加载方法根据需要用这些对象的数组填充User对象。我相信用面向对象的术语讲,这是对象聚合,但是我已经看到了各种命名不一致的问题,并且不关心就精确命名约定发动战争。

现在,考虑一下我有一些松散耦合的对象,这些对象可能会/可能不会填充,这取决于应用程序的需求。

从REST的角度来看,我正在尝试确定应采用的方法。这是我目前的想法(暂时仅考虑GET):

选项1-完全填充对象:

GET api.example.com/user/{user_id}

读取User对象(资源),并返回带有所有可能的Channel和Member对象的用户对象,并预先加载和编码(JSON或XML)。

优点:减少对象数量,无需遍历对象层次结构
缺点:必须完全填充对象(昂贵)

选项2-填充主要对象,并包括指向其他​​对象资源的链接:

GET api.example.com/user/{user_id}

读取用户对象(资源),并返回用户对象填充的用户数据和两个列表。

每个列表都引用适当的(子)资源,即

api.example.com/channel/{channel_id}
api.example.com/member/{member_id}

我认为这与超媒体的含义很接近(或完全一样)-客户端可以根据需要获取其他资源(只要我合理地标记它们)。

优点:客户端可以选择加载下属,否则,可以更好地分离对象作为REST资源。
缺点:需要进一步的行程才能获得辅助资源

选项3-启用递归检索

GET api.example.com/user/{user_id}

阅读用户对象,并包括指向子对象列表的链接,即

api.example.com/user/{user_id}/channels
api.example.com/user/{user_id}/members

/ channels调用将以以下形式返回频道资源列表:

api.example.com/channel/{channel_id}

优点:主要资源公开了获取子目录的位置,而不是子目录(更RESTful?),不需要先要求下级,下级列表生成器(/ channels和/ members)提供了接口(类似方法)响应更多的服务喜欢。
缺点:现在需要三个调用才能完全填充对象

选项4-(重新)考虑REST的对象设计

我正在重用[现有的]应用程序对象层次结构,并尝试将其应用于REST-或更直接地,为其提供API接口。

也许REST对象的层次结构应该有所不同,或者新的RESTful思想正在暴露现有对象设计的局限性。

对以上任何想法表示欢迎。

非常感谢

保罗


在我搜索我也发现了这组文章,我发现非常有用的和可访问:infoq.com/minibooks/emag-03-2010-rest
paulkmoore

如果您正在寻找所有这些样式的基于JSON的媒体类型,请考虑Shoji:aminus.org/rbre/shoji/shoji-draft-02.txt
fumanchu 2010年

Answers:


41

没有理由不将它们结合在一起。

  • api.example.com/user/{user_id} –返回用户表示
  • api.example.com/channel/{channel_id} –返回频道表示
  • api.example.com/user/{user_id}/channels –返回频道代表列表
  • api.example.com/user/{user_id}/channel_list –返回频道ID列表(或使用上述链接指向其完整表示的链接)

如有疑问,请考虑如何向没有“ API”问题的人类用户显示数据:用户既需要索引页({user_id}/channel_list)又需要完整视图({user_id}/channels)。

一旦有了它,只需支持JSON而不是HTML(或除了HTML之外)作为表示形式,就可以使用REST。


Piet-非常感谢,它非常有用。我想阐明从人的角度遍历数据的要点。我的想法是,如果我提供“下一个”资源列表而不是“完整视图”,则允许用户细读他们想要细读的内容,因此提供的链接列表是“更多”的RESTful(更适合HATEOS) )。我可以理解,出于性能原因,提供填充列表很有用。我知道这在某种程度上取决于用例,但是我想知道暂时是否最好使接口更简单(也许更慢)?
paulkmoore,2010年

3
您如何创建此类分层对象?例如,假设我们有图书馆->书籍->章节->页面。现在一种方法是创建一个庞大的库对象,但是有什么选择呢?如果应该先创建库,则先获取ID,然后再预订,依此类推。
Pradeep Kumar Mishra 2012年

Qiuck问题:第一个@Path或RequestMapping匹配是否获得所有内容?如果是这样,第二个API将永远不会执行,对吗?我应该将更通用的API放在代码的最后还是放在我的前面?–
史蒂夫·斯蒂尔森

29

我能提供的最佳建议是尝试避免在公开对象时考虑REST api。您创建的资源应支持所需的用例。如有必要,您可以为所有三个选项创建资源:

api.example.com/completeuser/{id}
api.example.com/linkeduser/{id}
api.example.com/lightweightuser/{id}

显然,我的名字有点愚蠢,但是您叫什么名字真的没关系。这个想法是针对特定的使用场景,您使用REST api以最合逻辑的方式呈现数据。如果存在多个方案,请根据需要创建多个资源。我喜欢将我的资源更像UI模型而不是业务实体。


Darrel-谢谢您-也非常有用。我怀疑我缺少的对象映射周围有些微妙之处。我得出的结论是,尽管我可以调用现有的对象框架,但我需要仔细设计“资源视图”。谢谢。
paulkmoore,2010年

10

我会推荐Restful Obects,这是暴露域模型的Restful的标准

Restful Objects的想法是为域对象模型提供一个标准的通用RESTful接口,使用JSON公开其结构表示,并使用HTTP GET,POST,PUT和DELETE与域对象实例进行交互。

根据标准,URI将类似于:

  • api.example.com/object/user/31
  • api.example.com/object/user/31/properties/用户名
  • api.example.com/object/user/31/collections/channels
  • api.example.com/object/user/31/collections/members
  • api.example.com/object/user/31/actions/someFunction
  • api.example.com/object/user/31/actions/someFunction/invoke

还有其他资源

  • api.example.com/services
  • api.example.com/domain-types

该规范定义了一些主要的表示形式:

  • 对象(代表任何域对象或服务)
  • 列表(到其他对象的链接)
  • 属性
  • 采集
  • 行动
  • 动作结果(通常包含对象或列表,或仅包含反馈消息)
  • 以及少量的次要表示形式,例如家庭和用户

这很有趣,因为您会看到表示是完全自描述的,从而在需要时提供了实现通用查看器的可能性。

或者,可以通过定制应用程序直接使用这些表示。


6

这是我经过数小时的搜索得出的结论,并得到了响应者的反馈:

如果我的对象实际上是一个多部分对象,则需要将其视为单个资源。因此,如果我得到对象,则所有下属都应存在。为了使资源可缓存,这是必需的。如果我部分加载对象(并提供ETag标记),则其他请求者可能会在期望完整对象时收到部分对象。 结论-如果将对象用作资源,则应完全填充对象。

关联的对象关系应作为指向其他(主要)资源的链接而可用。这样,可以通过遍历API来发现对象。

同样,对于主应用程序站点有意义的对象层次结构可能并不是您需要以RESTful方式进行操作的对象,而是更有可能揭示现有层次结构的问题。话虽如此,API可能需要比以前设想的更多的专用用例,并且可能需要专用资源。

希望可以帮助某人

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.