过滤条件的查询字符串与资源路径

Query String vs Resource Path for Filtering Criteria

背景

我有 2 个资源:coursesprofessors

A course 具有以下属性:

一个professor具有以下属性:

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

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


窘境

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

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

不过我不确定该用哪个。


当前解决方案

目前,我正在使用后者的增强形式。我的推理是,如果我想添加 filtering/sorting 标准,则更多 scale-able。

我实际上是encoding/embedding JSON 字符串进入查询参数。因此,一个(解码的)示例可能是:

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

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

虽然我从来没有见过有人这样做,所以我想知道我是否在做一些愚蠢的事情。


结束问题

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


经过编辑以减少基于意见的内容。 (查看评论)

正如在之前的评论中所解释的那样,REST 不太关心标识唯一资源的 link 的实际形式,除非 RESTful constraints 或超文本传输​​协议(HTTP ) 本身就被侵犯了。

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

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

如果参数定义了类似特定风格的东西 属性(即汽车颜色),我更愿意将它们放入矩阵参数中。它们也可以出现在 URI 的中间,即 /api/professors;hair=grey/courses 可以 return 所有由头发颜色为灰色的教授举办的课程。

在我的理解中,路径参数是应用程序完成请求所需的强制参数,否则将不会首先在服务端调用相应的方法处理程序。通常这是一些资源标识符,例如 table-行 ID 或分配给特定实体的 UUID。

关于描述关系,我通常从 1:n 关系的第一部分开始。如果我面临 m:n 关系,就像您与教授的情况一样 - 课程,我通常会更容易地从可能存在的实体开始。教授仍然是教授,即使他没有举办任何讲座(在特定期限内)。如果没有可用的教授,课程就不会成为课程,我会把教授放在课程之前,尽管关于 REST 课程仍然是很好的顶级资源。

因此我会更改您的查询

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

我稍微更改了您字段的语义,因为年份 属性 可能更适合课程而不是教授资源,因为教授已经通过教授 ID 缩减为单一资源。但是,这些课程应仅限于仅包括 2016 年举办的课程。由于排序是可选的,并且可能指定了默认值,因此我非常适合将其放入查询参数部分。排序的字段与排序本身有关,因此也属于查询参数。我已将年份放入矩阵参数中,因为这是课程本身的某个 属性,例如汽车的颜色或汽车的制造年份。

但如前所述,这是相当固执己见,可能与您或其他人的观点不符。

I could use either of the following:

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

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

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

但是人类,盯着 HTTP 流量日志,不会不透明地对待 URI——我们实际上是在试图弄清楚正在发生的事情的上下文。远离试图追踪 bug 的可怜混蛋对 URI 设计来说是一个很好的 属性。

这对于让您的 URI 设计易于猜测也很有用 属性。根据一些简单一致的原则设计的 URI 比随意设计的 URI 更容易使用。

Programmers

对路径段与查询进行了很好的概述

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

是否可以对同一个“答案”的所有 return 表示。

另一方面,选择增加了复杂性。为您的客户提供不止一种做事的方式可能是也可能不是一个好主意。 “别让我想!”

在 /other/other 方面,api 具有一堆“通用”原则并带有一堆任意例外情况,并不像具有一致原则和一些重复(需要引用)。

“规范”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"}

I've never seen anyone do it this way though, so I wonder if I'm doing something stupid.

我也没有,但在语法上它看起来有点像 GraphQL。我看不出有什么理由不能以这种方式表示查询。作为单个查询描述对我来说更有意义,而不是将其分成多个部分。当然,它需要 URL 编码等

但我不想为这个权利发疯,除非你真的需要给你的客户那种灵活性。还有更简单的设计(见 Roman 的回答)