过滤条件的查询字符串与资源路径
Query String vs Resource Path for Filtering Criteria
背景
我有 2 个资源:courses
和 professors
。
A course
具有以下属性:
- id
- 主题
- semester_id
- 年
- 节
- professor_id
一个professor
具有以下属性:
- id
- 教师
- super_user
- first_name
- last_name
所以,你可以说一门课程有一个教授,一个教授可能有很多门课程。
如果我想获得所有课程或所有教授,我可以分别:GET /api/courses
或 GET /api/professors
。
窘境
当我想获得某个教授教授的所有课程时,我的困惑就来了。
我可以使用以下任一方法:
GET /api/professors/:prof_id/courses
GET /api/courses?professor_id=:prof_id
不过我不确定该用哪个。
当前解决方案
目前,我正在使用后者的增强形式。我的推理是,如果我想添加 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
对路径段与查询进行了很好的概述
当然,如果您有两个不同的 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 的回答)
背景
我有 2 个资源:courses
和 professors
。
A course
具有以下属性:
- id
- 主题
- semester_id
- 年
- 节
- professor_id
一个professor
具有以下属性:
- id
- 教师
- super_user
- first_name
- last_name
所以,你可以说一门课程有一个教授,一个教授可能有很多门课程。
如果我想获得所有课程或所有教授,我可以分别:GET /api/courses
或 GET /api/professors
。
窘境
当我想获得某个教授教授的所有课程时,我的困惑就来了。
我可以使用以下任一方法:
GET /api/professors/:prof_id/courses
GET /api/courses?professor_id=:prof_id
不过我不确定该用哪个。
当前解决方案
目前,我正在使用后者的增强形式。我的推理是,如果我想添加 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
对路径段与查询进行了很好的概述当然,如果您有两个不同的 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 的回答)