表现层状态转换风格接口指北 RESTful APi Guide

写这一篇文章的最大原由还是看见了不知何人在 aws api gateway 中的豪放做法后,决定必须留下这一篇指北。

在看到满篇开头的“动作词”和“令人愉悦”的无层级结构后...

在看到满篇开头的“动作词”和“令人愉悦”的无层级结构后,我很难描述我当时的心情,但我很明确的知道,一顿重构是跑不了了。所以也顺带来说一说这个所谓的“表现层妆台转换风格”,Representational State Transfer 到底是个什么东西,以及在 APi 设计中应该怎样去运用。在这里会引用 Github APi 的设计方式,并且会通过我对原有选课平台 APi 的重构来做具体说明。

表现层状态转化?

表现层状态转化实指的是所有的资源都有不同的表现形式,但是我们需要区分“资源”和它的“表现形式”。举一个简单的例子,“李青的教学视频.mp4”,我们可以把后缀名前的文件名理解为资源,而后缀名理解为表现形式,对应了我们一个请求中的 URI 和 Accept-Content-Type 内容。然后我们可以对资源进行状态转换的操作,这个操作就可以通过 HTTP 请求中定义的那些“动作”来执行啦~。比如 GET 用来请求资源,PUT 用于新建资源,PATCH 用来更新资源,DELETE 用于删除资源。

这里说完,看向以前的错误示例,其最明显的错误当然就是在 URI 中包含了“状态转换词”,即与 RESTful 里想要的 URI 不包含动词背道而驰。所有的动词都应该以 HTTP 动作包含在 HTTP 协议之中。

RESTful APi 设计

为了设计一个真正好看,好用还符合规范的 RESTful APi,我们还需要知道几条“行业潜规则”。

1. 我们通常会使用与主域名不同的专用域名部署 APi,在简单时可以使用单独路径前缀,同时将版本放入 URI
https://api.richku.com/v1
https://richku.com/api/v1

这两者在实际生产环境中都是可以接受的,当然在可行的时候我们也倾向于直接将版本号加入 HTTP 的 Accept 请求头里。比如 Github 在支持前种 URI 包含版本号的同时,也可使用 HTTP Accept 头区分

Accept: application/vnd.github.v3+json

这样来限制 v3 版本的 APi。

2. 我们 URI 中的结尾通常是复数名词

其原因在于我们想与数据库中的数据表名称对应,也应为一个数据表通常存储着同类型的内容,就像数学中的一个“集合”

https://api.richku.com/v1/courses
https://api.richku.com/v1/users

同时资源可以进行嵌套

你可以像 RIC 这样
GET /users/:user/comments                        列出某个用户的 comments

或是像 Github 这样
GET /orgs/:org/issues                            列出某个项目的 issue

我们使用冒号开始来代表变量
3. 动作用 HTTP 状态表示

就像开头说的千万别出现如 createCourse, addComment 这种 URI,你能把 jzy 气个半死

常用的 GET 请求一个或多个资源,POST 创建一个资源,PUT 完整的更新一个资源,PATCH 不完整的更新一个资源,DELETE 删除一个资源。

比如像 Github 这样
GET /issues                                      列出所有的 issue
GET /orgs/:org/issues                            列出某个项目的 issue
GET /repos/:owner/:repo/issues/:number           获取某个项目的某个 issue
POST /repos/:owner/:repo/issues                  为某个项目创建 issue
PATCH /repos/:owner/:repo/issues/:number         修改某个 issue
PUT /repos/:owner/:repo/issues/:number/lock      锁住某个 issue
DELETE /repos/:owner/:repo/issues/:number/lock   解锁某个 issue

以及像崭新的 RIC 这样
GET /courses/:course/comments                    列出某个课程的 comments
POST /courses/:course/comments                   为某个课程新建一个 comment
DELETE /courses/:course/comments/:comment        万恶的管理员来删评啦~

同时需要记住,大多数涉及要修改资源的情况,我们通常不会使用 GET 请求,除非 GET 在某一个特定场景之中真的是非常的方便,比如你想要统计一篇文章的阅读量,虽然严格来说我们并没有为了文章阅读量增加而新建一个 APi,这个请求的后端操作是与请求文章资源的请求挂靠的。

4. HTTP 响应状态码

在这里可以参照使用 HTTP 官方提供的众多状态码如 200,404 等标注你的请求,你都可以通过在谷歌或者百度搜索 HTTP 状态码来查询到它们,这也是我最为推荐的方式。当然,任性的你也可以自己写自己的状态码,但是一定要为它们准备详尽的文档,不然下一个接手的倒霉蛋真的会面对奇奇怪怪的数字束手无策。

这里我就留下一个链接吧:点我点我~

5. 万能的 Filter

我们需要为一些资源来提供一定的过滤器来实现更加精准的数据展示,当然也可能是为了排序与分页。它们会以参数的方式传入。

6. 频率与文档

古人有云做万事皆要有度,为了你网站的 APi 不被恶意份子分分钟干的什么都不剩,给他加上频率限制吧,当然可以通过 Route Group 来区分不同资源受到不同的频率限制。显然大多数 GET 请求的频率会比 POST 请求更高。

还有万能的文档,这是前端开发者的狗命,现在就握在你的手中,当然我相信下班后你的狗命就会握在他们手中了。所以为了对方以及自己都能好好活着,准备一份详尽的文档吧,使用 Postman 保存好你调试的 Request,再为他们分上组写上一些备注就已经足够清晰啦~

Default image
JDScript
HKU CS 人下人 + 真的是个废物