轻松正确理解并上手RESTful
指导准则:类似电脑里面的文件夹位置
# 1. RESTful 的概念
REST 的英文全称 Representational State Transfer,即“表现层状态转移”。
REST 的名称“表现层状态转化”中,省略了主语,“表现层”其实指的是“资源”(Resources)的“表现层”。
**所谓“资源”,就是网络上的一个实体,或者说是网络上的一个一个具体信息。**它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。
HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。他们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源(也可以用来更新资源),PUT 用来更新资源,DELETE 用来删除资源。
最常见的一种设计错误,就是 URI 包含动词。因为“资源”表示一种实体,所以应该是名词,URI 不应该有动词,动词应该放在 HTTP 协议中。
# 2. CRUD 与 RESTful 对应的操作
在 REST风格中,对于增删改查 CRUD(即:create、read、update、delete),分别对应如下:
操作 | RESTful |
---|---|
create | POST |
read | GET |
update | PUT |
delete | DELETE |
各种 HTTP 方法成功处理后的数据格式: |
请求方式 | response格式 |
---|---|
GET | 单个对象、集合 |
POST | 新增成功的对象 |
PUT/PATCH | 更新成功的对象 |
DELETE | 空 |
常用的http状态码及使用场景:
状态码 | 使用场景 |
---|---|
400 bad request | 常用在参数校验 |
401 unauthorized | 未验证的用户,常见于未登录。如果经过验证后依然没权限,应该403(即authentication和authorization的区别) |
403 forbiden | 无权限 |
404 not found | 资源不存在 |
500 internal server error | 非业务类异常 |
503 service unavalibale | 有容器抛出,自己的代码不要抛出这个异常 |
# 3. RESTful 的规范
- 不能出现动词
- URI 中的名词根据对应的名词是否存在单个还是多个进行确定是否是单数还是复数
- 可以用中杠
-
或者下杠_
,更加推荐用中杠-
# 4. 异步任务
对耗时的异步任务,服务器接受客户端传递的参数后,应返回成功的任务资源,其中包含了任务的执行状态。科幻可以轮询改任务获得最新的执行进度。
提交任务:
POST /batch-publish-msg
[{"from":0, "to": 1, "text": "abc"}, {}, {{}}]
返回:
{"taskId": 3, "createBy": "Anonymous", "status": "running"}
让客户端查询该任务的状态的接口:
GET /task/3
{"taskId": 3, "createBy": "Anonymous", "status": "success"}
如果任务的执行状态包括较多信息,可以把"执行状态"抽象成组合资源,客户端查询该状态资源了解任务的还行情况。
GET /task/3/status
{"progress": "50%", "total": 18, "success": 8, "fail": 1}
# 5. 避免层级过深的 URI
在 url 中表达层级,用于按实体关系进行对象导航,过度的导航容易导致 url 膨胀,
错误:
GET /zoos/1/areas/3/animals/4
尽量使用查询参数代替路径中的实体导航,如:
GET /animals?zoo=1&area=3
组合实体不是 first-class 的实体,它的生命周期完全依赖父实体,无法独立存在,在实际上通常是数据库表中某些抽象,不直接对应表,也无 id。一个常见的例子是 User-Address,Address 是User 表中 zipCode/country/city 三个字段的简单抽象,无法独立于 User 存在,必须通过 User 索引到 Address,如:
GET /user/1/addresses
资源的地址推荐用嵌套结构。比如:
GET /friends/10375923/profile
# 6. 举例说明
在我们项目中,有个同事这么设计问卷系统的 API 设计:
# 6.1 C端的项目名称叫做 client-user-behavior
通常 url 的前缀是带上项目的(因为项目名称具有特定的含义且具有唯一性)
1. 用户获取问卷信息 GET /client/questionnaire/paper/{paper_id}
优化为:
GET /client-user-behavior/questionnaire/papers/{paper_id}
** 说明:** 最好加上项目维度,项目的子模块questionnaire用单数,因为只存在一个,papers用复数,因为问卷不止一个
2. 用户提交问卷答案信息 POST /client/questionnaire/answer
优化为:
POST /client-user-behavior/questionnaire/papers
**说明:**用户提交问卷的主体应该是问卷,answer是附属的信息
# 6.2 A 端的项目名称叫做 admin-questionnaire
1. 分页查询问卷列表 GET /admin/questionnaire/paper
优化为:
GET /admin-questionnaire/questionnaire/papers
** 说明**:这个是业内共识,RESTful风格获取列表信息都这么写
2. 查看问卷详情(包含问卷基本信息和问题配置) GET /admin/questionnaire/paper/{id}
优化为:
GET /admin-questionnaire/questionnaire/papers/{paper_id}
**说明:**查看问卷详情中的{id}推荐用{paper_id},可以简洁明了的知道是哪个id
3. 复制问卷 POST /admin/questionnaire/paper/copy
这个接口可以和查看问卷详情公用,有特殊需要一定要写新的接口,也应该是GET方式
4. 编辑问卷题目 PUT /admin/questionnaire/paper/{id}/question
优化为:
PUT /admin-questionnaire/questionnaire/papers/{paper_id}/questions/{question_id}
5. 新增问卷基本信息 POST /admin/questionnaire/paper
优化为:
POST /admin-questionnaire/questionnaire/papers/base_info
6. 编辑问卷基本信息 PUT /admin/questionnaire/paper/{id}
优化为:
PUT /admin-questionnaire/questionnaire/papers/{paper_id}/base_info
7. 删除问卷 DELETE /admin/questionnaire/{id}
优化为:
DELETE /admin-questionnaire/questionnaire/papers/{paper_id}
8. 问卷启用禁用 PUT /admin/questionnaire/{id}/status
优化为:
PUT /admin-questionnaire/questionnaire/papers/{paper_id}/statuses/{status}
9. 查看问卷答题明细列表动态表头 GET /admin/questionnaire/{id}/question_title
优化为:
GET /admin-questionnaire/questionnaire/papers/{paper_id}/question_titles
10. 问卷答题明细导出 GET /admin/questionnaire/{id}/answer_record_export
优化为:
GET /admin-questionnaire/questionnaire/papers/{paper_id}/export