带相关资源的RESTful API

本文关键字:RESTful API 资源 | 更新日期: 2023-09-27 18:04:38

我正在设计我的第一个真正的RESTful API,它将位于WCF服务之上。

有像;出口,计划和工作。调度始终由出口拥有,并且调度将包含0个或多个作业。一项工作不一定要有时间表。

我一直认为资源应该以与文件系统上资源的寻址方式相同的方式进行寻址。这意味着我将拥有像这样的URI:

/outlets
/outlets/4/schedules
/outlets/4/schedules/1000/jobs
/outlets/4/schedules/1000/jobs/5123

当考虑如何在不同的情况下收回资源时,事情开始变得混乱。

。我想要一份没有时间表的工作:

/outlets/4/jobs/85(这现在意味着我们有两种方法将工作拉回计划中)

。我想要所有的时间表,不管outlet:

/schedules/outlets/ALL/schedules

还有很多其他更复杂的需求,但我相信你已经明白了要点。

文件系统有一个很好的、合乎逻辑的资源寻址方式。显然,你可以创建符号链接,并实现类似于我所描述的东西,但它会很混乱。一旦事情变得更加复杂,比如添加按日期获取日程安排的功能,它就会变得更加混乱:

/outlets/4/2016-08-29/schedules

如果不使用查询字符串参数,我甚至不确定我如何请求回所有不在时间表上的工作。因为unscheduled不是资源,所以下面的感觉是错误的:

/outlets/4/unscheduled/jobs

所以,我开始认为文件系统类型寻址只适用于最简单的服务(我们的底层系统有数百个实体类型,有一些非常复杂的关系和大量的操作)。

用多种方法做同一件事往往会导致混乱和凌乱的文档,我想避免这种情况。因此,我几乎被迫选择使用最小公分母和选择非常简单的地址形式-就像下面的第三个:

/outlets/4/schedules/1000/jobs/5123
/outlets/4/jobs/5123
/jobs/5123

从这些非常简单的地址形式中,我需要扩展使用查询字符串参数来做更复杂的事情,例如:

/jobs?scheduleId=1000
/jobs?outletId=4
/jobs?outletId=4&fromDate=2016-01-01&toDate=2016-01-31

这感觉像是违背了REST模型,像这样的查询字符串参数是不可预测的,与"不需要文档"的想法相距甚远。

好吧,所以现在我几乎站在栅栏的一边,就是说为了得到一个干净的,可维护的API,我将不得不使用非常简单的资源地址,广泛使用查询字符串参数,并有良好的文档。

不管怎样,这感觉不像是我应该得出的结论。我哪里做错了?

带相关资源的RESTful API

欢迎来到REST API编程的世界。这些都是我们在试图将一般原则应用于具体情况时所面临的难题。对于你的问题,没有明确而简单的答案,但这里有一些额外的提示可能会有用。

首先,你是对的,当你有复杂的关系时,文件系统解决问题的方法就失效了。只有当那里有真正的层次时,您才会想要建立那种寻址。

例如,如果所有作业都是单个调度的一部分,那么查找schedules/{id}/jobs/{id}以获得给定的作业是有意义的。如果从数据存储的角度考虑,可以想象每个调度都有一个XML文件,作业只是该文件中的元素。

然而,在这种特殊情况下,听起来您的数据更像是关系。从数据存储的角度来看,您可以将每个作业表示为数据库表中的一行,并建立一些外键关系,将一些作业绑定到一些调度。您的寻址方案应该通过使/jobs成为顶级端点来反映这一点,并在有意义的时候使用可选的查询字符串参数按调度或出口进行过滤。

所以你在正确的轨道上。您可能还要考虑的另一件事是OData,它扩展了基本的REST原则,使用面向标准的方式来表示诸如使用查询字符串参数进行过滤之类的事情。地址语法感觉有点"过时",但它在处理直接REST开始崩溃的情况方面做得很好。由于它更加标准化,因此有一些工具可以帮助完成从数据层转换为OData端点,或基于该端点公开的元数据生成客户端代理助手等工作。

这感觉像是违背了REST模型,像这样的查询字符串参数是不可预测的,与"不需要文档"的想法相距甚远。

如果您使用OData,那么它的规范将与您的工具生成的元数据结合起来,成为您的文档。例如,您的元数据说job具有表示日期的date属性。然后,OData规范提供了一种表示日期值的筛选查询的方法。根据这些信息,消费者可以可靠地生成一个过滤器查询,该查询将"正常工作",因为您正在使用框架服务器端来完成困难的部分。如果他们不想记住OData URL是如何工作的,他们可以用自己选择的语言生成一个客户端代理,这样他们就可以通过自己喜欢的语法生成合适的URL。