如何设计一个多条件查询的restfulAPI?

假设数据库里有这样的表:
1、studnet表

主键姓名性别年龄班级ID
1张三1031
2李四1142

2、班级表

主键班级名称
31三(一)班
42四(二)班

查询时,除可按ID查询,亦可按性别、年龄、班级进行相似性查找,

如果按restful要求,

查询id为1的student可以为
GET /students/1

查询班级31里所有的学生可以为
GET /classes/31/students

那么,查询班级31里所有年龄为10岁的学生该怎么写呢
GET /classes/31/students/age/10 ?

阅读 15.5k
5 个回答

RESTful 是让你把资源映射的 URI 中,这里 classes(班级) 可以作为一个资源,students(学生),可以视作是班级下的资源

所以 GET /classes/31/students 看起来是合理的,但是 GET /classes/31/students/age/10 可就不那么合理了, age 是一个资源吗?很明显,他不算是一个资源。所以就不能放到 URI 里面去。

RESTful 并不是说完全排斥使用查询参数,而是合理分配。显然,在这里使用 GET /classes/31/students?age=10 更加符合语义。

给你一个实践吧:

基础的资源路径:

  • 创建 Create:POST /students
  • 查询 Query:GET /students?age=0,10&name=姓,&q=搜索
  • 获取 Get: GET /students/{studentId}
  • 删除 Destroy:DELETE /students/{studentId}
  • 替换 Update:PUT /students/{studentsId}
  • 补丁 Patch(部分更新):PATCH /students/{studentsId}
  • 替换单个属性 Update Field:PUT /students/{studentId}/{fieldName},请求的 payload 就是对象的字段的值
  • 班级的对应的方式与 Student 的一样,POST /classes
  • 班级下的学生: POST /classes/{classId}/students 向班级中添加学生
  • 查询班级下的学生:GET /classes/{classId}/students?age=0,10&name=,姓,&q=搜索
  • 获取班级下学生的详细信息:GET /classes/{classId}/students/{studentId}
  • ……

我们将接口分为以下几种类型:

  1. 创建资源:Create,统一为 POST /resource-names
  2. 查询资源:Query,统一为 GET /resource-names?q=查询关键字&age=0,10&name=,姓&limit=10&offset=0
  3. 获取资源:Get,统一为 GET /resource-names/{recourceId}
  4. 删除资源:Destroy,统一为 DELETE /resource-names/{recourceId}
  5. 替换资源:Update(全量更新),统一为 PUT /resource-names/{recourceId}
  6. 部分更新:Patch(打补丁),统一为 PATCH /resouce-names/{resourceId}/{?fieldName}
  7. 操作请求:Action(比如通过审核、激活等对资源的操作)统一为 POST /resouce-names/{resourceId}/action-name
  8. 大型查询:Maga Query,比如我们希望从磁带库中查询某一个时间段的数据,可能查询需要很长的时间,那么按以下方式完成:
    a. 创建查询: POST /queries,创建一个新的查询,payload 为查询所需要的所有配置参数信息,接口返回 queryId
    b. 通过 queryId 获取状态(或者通过 WebSocket 直接通知响起方), GET /queries/{queryId}/status
    c. 通过 queryId 获取查询结果 GET /queries/{queryId}/rows?limit=10&offset=0&q=搜索,查询结果得到之后,同样可以对结果进行二次搜索

关于查询参数的规范:

  • age=0,10 表示查询 age[0, 10) 区间的数据, , 区分起始,同样的还可应用于 时间日期 等类型的数据
  • age=,10;20,30;40表示查询 age[0, 10)以及[20,30)以及大于 40 的数据, , 前面表示开始值,若不提供,表示所有小于 , 右侧的值的值,, 右侧表示结束值,若没有,则表示所有大于 , 左侧的值,; 表示多个值的区分。
  • age=10 表示 age===10 的值
  • name=姓, 表示以 开头的值,类似 LIKE %姓
  • name=,名 表示以 结尾的值
  • name=姓名 表示 name 完全等于 姓名 的值
  • limit 此次查询多少条数据
  • offset 从 0 开始的偏移量
  • q 万能的搜索关键字,后端根据需求选择性使用

RESTful 并不排斥使用查询参数呀,你就正常 [GET] /students?class_id=31&age=10 好了,需要哪个查询条件就传哪个查询条件,组合条件就都传。

建议 [POST] /classes/31/students/_find,body里传{'age': 13}这样的json对象
这个做法明显不优雅,但是非常稳定,因为:

  1. url参数要编码解码,遇到一些不靠谱的对接方会非常难受,浪费时间
  2. url参数传递有层次的数据(对象),多值的数据(数组)都不好弄

对于无固定格式的查询,比较合适的方式是传入 JSON 作为查询条件,而不是将查询条件硬拼在 URL 中。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题