我应该如何处理 RESTful API 中的对象层次结构?

Posted

技术标签:

【中文标题】我应该如何处理 RESTful API 中的对象层次结构?【英文标题】:How should I deal with object hierarchies in a RESTful API? 【发布时间】:2011-04-21 12:11:02 【问题描述】:

我目前正在为现有的 php 应用程序设计 API,为此我正在研究 REST 作为一种合理的架构方法。

我相信我对关键概念有合理的把握,但我很难找到任何处理过对象层次结构和 REST 的人。

问题来了……

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

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

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

现在,考虑一下我有一些松散耦合的对象,我可能会/可能不会根据应用程序的需要填充它们。

从 REST 的角度来看,我试图确定应该采用什么方法。这是我目前的想法(暂时只考虑GET):

选项 1 - 完全填充对象:

GET api.example.com/user/user_id

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

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

选项 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
    

PROS:主要资源公开了从哪里获取下属,但不公开它们是什么(更 RESTful?),不需要预先获取下属,下属列表生成器(/channels 和 /members)提供接口(方法喜欢)使响应更像服务。 缺点:现在需要三个调用来完全填充对象

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

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

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

欢迎对以上内容提出任何想法。

【问题讨论】:

在我的搜索中,我还发现了这组文章,我发现它们非常有用且易于访问:infoq.com/minibooks/emag-03-2010-rest 如果您正在为所有这些样式寻找基于 JSON 的媒体类型,请考虑 Shoji:aminus.org/rbre/shoji/shoji-draft-02.txt 【参考方案1】:

没有理由不将这些结合起来。

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 作为表示格式,你就有了 REST。

【讨论】:

Piet - 非常感谢,它非常有用。我想从人类的角度澄清关于遍历数据的观点。我的想法是,如果我提供“下一个”资源列表而不是“完整视图”,我允许用户阅读他们想要阅读的内容,因此提供链接列表是“更多”RESTful(更符合 HATEOS )。但是,我可以理解,出于性能原因,提供填充列表很有用。我知道这在某种程度上取决于用例,但我想知道暂时保持界面更简单(也许更慢)是否更好? 如何创建这样的分层对象?例如假设我们有图书馆 -> 书籍 -> 章节 -> 页面。现在一种方法是创建一个庞大的库对象,但有什么替代方法?如果您首先创建库,获取 id 然后进行预订等等。 Qiuck 问题:第一个 @Path 或 RequestMapping 匹配是否得到所有内容?如果是这样,第二个 API 将永远不会被执行,对吧?我应该将更通用的 API 放在代码的最后还是最前面? ——【参考方案2】:

我能给出的最佳建议是尽量避免将 REST api 视为暴露对象。您创建的资源应该支持您需要的用例。如有必要,您可以为所有三个选项创建资源:

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

显然我的名字有点傻,但你叫他们什么并不重要。这个想法是您使用 REST api 以针对特定使用场景的最合乎逻辑的方式呈现数据。如果有多个场景,则根据需要创建多个资源。我喜欢把我的资源更像是 UI 模型而不是业务实体。

【讨论】:

Darrel - 谢谢你 - 也非常有用。我怀疑我缺少的对象映射有一些微妙之处。我的结论是,虽然我可以调用我现有的对象框架,但我需要仔细设计我的“资源视图”。谢谢。【参考方案3】:

我会推荐Restful Obects,它是公开领域模型宁静的标准

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

根据标准,URI 将类似于:

api.example.com/object/user/31 api.example.com/object/user/31/properties/username 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

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

对象(代表任何域对象或服务) 列表(指向其他对象的链接) 财产 收藏 动作 动作结果(通常包含一个对象或一个列表,或者只是反馈消息) 还有少量的二级表示,例如 home 和 user

这很有趣,因为您会看到表示是完全自描述的,如果需要,可以实现通用查看器。

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

【讨论】:

【参考方案4】:

这是我经过数小时的搜索以及此处响应者的输入得出的结论:

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

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

此外,对主应用程序站点有意义的对象层次结构可能看起来不是您需要以 RESTful 方式行事,但更有可能揭示现有层次结构的问题。话虽如此,API 可能需要比以前设想的更专业的用例,并且可能需要专门的资源。

希望对某人有所帮助

【讨论】:

以上是关于我应该如何处理 RESTful API 中的对象层次结构?的主要内容,如果未能解决你的问题,请参考以下文章

使用 JWT 令牌时应该如何处理 RESTful 身份验证?

如何处理使用 3rd-party 的 restful store api 和 js mvc 框架的安全性?

如何处理库中的函数弃用?

如何处理restful对接口安全性问题

如何处理 Flux 应用程序中的嵌套 API 响应?

如何处理 PHP 代码中的 HTTP/1.1 403 Forbidden