我应该如何处理 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 身份验证?