查询字符串与筛选条件的资源路径

时间:2016-07-19 23:46:54

标签: api rest http api-design

背景

我有2个资源:coursesprofessors

course具有以下属性:

  • id
  • 主题
  • semester_id
  • 部分
  • professor_id

professor具有以下属性:

  • id
  • faculty
  • SUPER_USER
  • 如first_name
  • last_name

所以,你可以说课程有一位教授,一位教授可能有很多课程。

如果我想获得所有课程或所有教授,我可以分别:GET /api/coursesGET /api/professors

困惑

当我想要获得某位教授所教授的所有课程时,我的困惑就来了。

我可以使用以下任何一种方法:

  • GET /api/professors/:prof_id/courses
  • GET /api/courses?professor_id=:prof_id

我不确定要使用哪个。

当前解决方案

目前,我正在使用后者的增强形式。我的理由是,如果我想添加过滤/排序标准,它更具可扩展性。

我实际上是在编码/嵌入JSON字符串到查询参数中。因此,(解码)示例可能是:

GET /api/courses?where={professor_id: "teacher45", year: 2016}&order={attr: "topic", sort: "asc"}

上述请求将检索教授使用2016年提供的professor_id教授(或当前正在)教授的所有课程,并根据主题标题按升序ASCII顺序排序。

我从未见过有人这样做过,所以我想知道我是不是做了些蠢事。

结束问题

是否有使用查询字符串与资源路径进行过滤条件的标准做法?过去有哪些更大的API?是否可以接受或鼓励同时使用两种范例(使两个端点都可用)?如果我确实应该使用第二范式,除了编码JSON之外,还有更好的组织方法吗?有没有人在他们的查询字符串中看到过使用JSON的另一个公共API?

编辑基于意见较少。 (见评论)

2 个答案:

答案 0 :(得分:3)

正如之前的评论中已经解释的那样,REST并不关心标识唯一资源的链接的实际形式,除非违反了RESTful constraints或超文本传输​​协议(HTTP)本身。 / p>

关于查询或路径(甚至矩阵)参数的使用完全取决于您。有no fixed rule何时使用什么,但只是个人偏好。

我喜欢使用查询参数,特别是当值是可选的并且不需要像JAX-RS这样的大量框架时,即允许定义默认值。查询参数通常被认为是为了避免缓存响应is more an urban legend then the truth,尽管某些实现可能仍然会忽略对包含查询字符串的URI进行缓存的响应。

如果参数定义了类似特定风味属性(即汽车颜色)的东西,我更喜欢将它们放入矩阵参数中。它们也可以出现在URI的中间,即/api/professors;hair=grey/courses可以返回由头发颜色为灰色的教授持有的所有资源。

路径参数是应用程序在我理解的意义上满足请求所需的必需参数,否则不会在服务端首先调用相应的方法处理程序。通常这是一些资源标识符,如表行ID,分配给特定实体的UUID。

关于描绘关系,我通常从1:n关系的1部分开始。如果我面对一个m:n的关系,就像在教授的情况下那样 - cources,我通常从可能存在的实体开始,而不是更容易。教授仍然是教授,尽管他没有任何讲座(在一个特定的术语中)。作为一门课程,如果没有教授可用,那么我会把教授放在cources之前,尽管关于REST cources仍然是很好的顶级资源。

因此,我会更改您的查询

GET /api/courses?where={professor_id: "teacher45", year: 2016}&order={attr: "topic", sort: "asc"}

类似于:

GET /api/professors/teacher45/courses;year=2016?sort=asc&onField=topic

我稍微改变了你的字段的语义,因为年份属性可能更适合课程,而不是教授资源,因为教授已经通过教授身份缩减为单一资源。然而,课程应仅限于2016年举办的课程。因为排序是相当可选的并且可能具有指定的默认值,所以这是我放入查询参数部分的完美候选。要排序的字段与排序本身有关,因此也属于查询参数。我把这一年放入矩阵参数中,因为这是课程本身的某种特性,比如汽车的颜色或汽车的制造年份。

但正如之前已经解释的那样,这是相当自以为是的,可能与您或其他人的观点不符。

答案 1 :(得分:2)

  

我可以使用以下任何一种方法:

     
      
  • GET / api / professorors /:prof_id / courses
  •   
  • GET / api / courses?professor_id =:prof_id
  •   

你可以。以下是一些需要考虑的事项:

计算机(特别是REST客户端)应该将URI视为不透明的东西;关于他们考虑其价值的最接近的是在解决期间。

但是人类,盯着HTTP流量的日志,不会不透明地对待URI - 我们实际上是在试图找出正在发生的事情的背景。不要试图追踪错误的那个可怜的混蛋,这对于URI设计来说是个不错的选择。

它也是一个有用的属性,可以让你的URI设计可猜测。从一些简单的一致原则设计的URI将比任意的更容易使用。

在程序员

上有一个很好的概述路径段与查询

https://softwareengineering.stackexchange.com/questions/270898/designing-a-rest-api-by-uri-vs-query-string/285724#285724

当然,如果你有两个不同的URI,那两个"都遵循规则",那么这些规则在做出选择方面没什么帮助。

支持多个标识符是一个有效的选项。获得特定表示的方法不止一种,这是完全合理的。例如,这些资源

/questions/38470258/answers/first
/questions/38470258/answers/accepted
/questions/38470258/answers/top

都可以返回相同的表示"回答"。

另一方面,选择增加了复杂性。为您的客户提供多种方式来做某件事可能是也可能不是一个好主意。 "不要让我思考!"

在/其他/另一方面,api与一堆" general"带有一堆任意异常的原则并不像具有一致原则和一些重复的原则那样容易使用(需要引用)。

"规范"的概念URI在SEO中很重要,它在API世界中具有类似性。 Mark Seemann有一篇关于self links的文章,涵盖了基础知识。

您可能还想考虑资源支持哪些方法,以及设计是否表明这些方法。例如,修改集合的POST是一种通常被理解的习语。因此,如果您的URI看起来像一个集合

POST /api/professors/:prof_id/courses

然后,客户更有可能在资源及其支持的方法之间建立关联。

POST /api/courses?professor_id=:prof_id

没有什么"错误"有了这个,但它并不是一个常见的惯例。

  

GET / api / courses?where = {professor_id:" teacher45",year:2016}& order = {attr:" topic",sort:" asc& #34;}

     

我从未见过有人这样做过,所以我想知道我是不是做了些蠢事。

我也没有,但从语法上来说它看起来有点像GraphQL。我不知道为什么你不能用这种方式代表查询。对我来说,作为单个查询描述更有意义,而不是将其分成多个部分。当然,它需要进行URL编码等。

但我不想为此疯狂,除非你真的需要给你的客户那种灵活性。有更简单的设计(见罗马答案)