我和我的团队正在重构REST-API,我遇到了一个问题。 为简洁起见,让我们假设我们有一个包含4个表的SQL数据库:教师,学生,课程和教室。 现在,通过引用相关项目的URL,可以在REST-API中表示项目之间的所有关系。例如,对于一门课程,我们可以有以下内容
{ "id":"Course1", "teacher": "http://server.com/teacher1", ... }
此外,如果问一个课程列表,认为有一个GET
是对/courses
的调用,我会得到一个参考列表,如下所示:
{
... //pagination details
"items": [
{"href": "http://server1.com/course1"},
{"href": "http://server1.com/course2"}...
]
}
这一切都很好,而且很干净,但是如果我想列出所有带有老师姓名的课程标题,并且我有2000门课程和500名老师,则必须执行以下操作:
我的问题是,此方法通过成千上万的REST-API调用创建了大量的网络流量,并且我必须重新实现数据库将更有效地执行的自然联接。 同事们说,这是实现REST-API的标准方法,但是相对简单的查询就变得很麻烦。
因此,我的问题是:
1.如果我们在课程中嵌套教师信息,那是不对的。
2.是否应列出物品,例如GET /courses
返回引用列表还是项目列表?
编辑:经过一番研究,我会说我所想到的模型主要对应于jsonapi.org中所示的模型。这是个好方法吗?
答案 0 :(得分:1)
我的问题是,此方法通过成千上万的REST-API调用创建了大量的网络流量,并且我必须重新实现数据库将更有效地执行的自然联接。同事们说,这是实现REST-API的标准方法,但是相对简单的查询就变得很麻烦。
您是否实际测量了每个请求产生的开销?如果没有,您怎么知道开销会太大?从面向对象的程序员的角度来看,独自执行每个调用听起来可能很糟糕,但是您的设计缺少一项重要的资产,可以帮助Web扩展到当前的大小:
缓存可以在多个级别上进行。您可以在API级别上执行此操作,或者客户端可以执行某些操作,或者中介服务器可以执行此操作。 Fielding甚至疯狂地限制了REST!因此,如果您要遵守REST体系结构的哲学,则还应该支持响应的缓存。缓存有助于减少必须由单个服务器计算甚至处理的请求的数量。在无状态通信的帮助下,您甚至可以引入大量服务器,这些服务器全部针对数十亿个请求执行计算,这些请求充当客户端的一个内聚系统。中间缓存可以进一步帮助减少实际到达服务器的请求数量。
作为一个整体的URI(包括任何路径,矩阵或查询参数)实际上是缓存的键。在接收到GET
请求后,即,应用程序检查其当前缓存是否已包含该URI的存储响应,如果存储的数据“足够新”,则代表服务器将存储的响应直接返回给客户端。 。如果存储的数据已超过新鲜度阈值,它将丢弃存储的数据,并将请求路由到下一跳(可能是实际的服务器,可能是进一步的中介)。
有时,找到适合缓存的资源有时可能并不容易,尽管大多数数据并不会很快改变以至于完全忽略了缓存。因此,至少应该普遍引入缓存,尤其是API产生的更多流量。
虽然某些媒体类型(例如HAL JSON,jsonapi,...)允许您将从相关资源收集的内容嵌入到响应中,但是嵌入内容有一些潜在的缺点,例如:
如果相关资源仅链接到而不是直接嵌入,则客户端肯定必须触发进一步请求以获取该数据,尽管实际上它更可能(部分地)由缓存提供服务,该缓存如前所述现在在整个帖子中进行了两次,减少了服务器上的工作量。除此之外,一个积极的副作用可能是,您可以了解客户真正感兴趣的内容(如果您运行中介缓存)。
- 如果我们在课程中嵌套教师信息,那是不对的。
这没错,但是如上所述可能并不理想
- 是否应列出项目,例如GET / courses返回引用列表还是项目列表?
这取决于。没有对与错。
由于REST只是Web中使用的交互模型的概括,因此基本上相同的概念也适用于REST。根据“项目”的大小,返回项目内容的简短摘要并添加指向项目的链接可能会有所帮助。相似的事情也在网络上完成。对于参加课程的学生的列表,这可能是名称及其入学编号,并且可能会要求该学生提供更多链接的链接细节,以提供实际的链接,客户端可以使用此语义上下文。决定调用这种URI是否有意义。
此类链接关系名称通过IANA,Dublin Core或schema.org之类的通用方法或RFC 8288 (Web Linking)中定义的自定义扩展名进行了标准化。对于上述课程的学生名单,您可以使用about
关系名称来提示客户,可以通过以下链接找到有关当前项目的更多信息。如果要启用分页功能,则first
,next
,prev
和last
的使用可以并且可能也应使用,依此类推。
这实际上是HATEOAS的全部内容。将数据链接在一起并赋予它们有意义的关系名称,以跨越资源之间的一种语义网。通过将事物简单地嵌入到响应中,这样的语义图可能难以构建和维护。
最后,基本上可以归结为实现选择,是要嵌入还是引用资源。我希望,我能对缓存的有用性及其带来的好处(特别是在大型系统上)以及为URI提供链接关系名称的好处有所了解,以增强所使用关系的语义上下文。在您的API中。
答案 1 :(得分:1)
我的问题是,此方法通过成千上万的REST-API调用创建了大量的网络流量,并且我必须重新实现数据库将更有效地执行的自然联接。同事们说,这是实现REST-API的标准方法,但是相对简单的查询就变得很麻烦。
您的同事们失去了密谋。
这是您的试探法-您将如何在网站上支持此用例?
您可能会通过定义一个新的网页来完成此工作,该网页将生成您需要的报告。您将运行查询,结果集将生成一堆HTML,然后按一下!客户以标准化的方式获得所需的信息。
REST-API是同一回事,更强调机器的可读性。创建具有架构的新文档,以便客户可以理解返回给他们的文档的语义,告诉客户如何找到该文档的目标uri,然后瞧瞧。
创建用于处理新用例的新资源是REST的 normal 方法。
答案 2 :(得分:0)
是的,我完全认为您应该设计类似于jsonapi.org的东西。根据经验,我会说“首选需要较少网络呼叫的解决方案”。如果网络呼叫的数量减少数量级,则尤其如此。 当然,如果请求/响应的大小变得不合理,它并不会消除限制请求/响应大小的需要。
现实生活中的解决方案必须具有适当的平衡。只要可以使用Clean API,它就很好。
所以在您的情况下,我会这样:
GET /courses?include=teachers
或
GET /courses?includeTeacher=true
或
GET /courses?includeTeacher=brief|full
在最后一个响应中,只能包含brief
的教师ID和full
的完整教师详细信息。