Laravel 项目开发规范 API 设计规范
参考资料
首先请熟悉以下的两个文档:
API 设计上有无法抉择的地方,应该参考 GitHub 的 API 文档:
GitHub 的 RESTful API 设计是业内比较知名的。
API 版本控制
所有的 API,早期设计时都 必须 考虑版本控制。
随着业务的发展,需求的不断变化,API 的迭代是必然的,很可能当前版本正在使用,而我们就得开发甚至上线一个不兼容的新版本,为了让旧用户可以正常使用,为了保证开发的顺利进行,我们需要控制好 API 的版本。
将版本号直接加入 URL 中:
https://api.example.com/v1
https://api.example.com/v2
https://api.example.com/v3
RESTful API
开发 API 时,必须使用 RESTful 规范来架构 API。
具体规则下面罗列出来。
1. 使用 URL 定位资源
必须使用 URL 定位资源的规则。
在 RESTful 的架构中,所有的一切都表示资源,每一个 URL 都代表着一种资源,资源应当是一个名词,而且大部分情况下是名词的复数,尽量不要在 URL 中出现动词。
先来看看 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
例子中冒号开始的代表变量,例如 /repos/summerblue/larabbs/issues
在 GitHub 的实现中,我们可以总结出:
-
资源的设计可以嵌套,表明资源与资源之间的关系。
-
大部分情况下我们访问的是某个
资源集合
,想得到单个资源
可以通过资源的 id 或 number 等唯一标识获取。 -
某些情况下,资源会是单数形式,例如
某个项目某个 issue 的锁
,每个 issue 只会有一把锁,所以它是单数。
❌ 错误的例子:
POST https://api.example.com/createTopic
GET https://api.example.com/topic/show/1
POST https://api.example.com/topics/1/comments/create
POST https://api.example.com/topics/1/comments/100/delete
✅ 正确的例子:
POST https://api.example.com/topics
GET https://api.example.com/topics/1
POST https://api.example.com/topics/1/comments
DELETE https://api.example.com/topics/1/comments/100
2. Laravel 中使用资源路由
Laravel 应该使用以下来定义资源路由:
Route::apiResource('users', UserController::class);
以上等同于:
Verb Path Action Route Name
GET /users index users.index
POST /users store users.store
GET /users/{user} show users.show
PUT|PATCH /users/{user} update users.update
DELETE /users/{user} destroy users.destroy
如果你不使用 apiResource()
方法,控制器方法 必须 按照以上的指纹来定义路由。
apiResource()
还可以使用以下方法来定制具体使用的路由:
Route::apiResource('photos', PhotoController::class)->only([
'index', 'show'
]);
Route:: apiResource('photos', PhotoController::class)->except([
'create', 'store', 'destroy'
]);
3. 使用 HTTP 动词描述操作
必须使用 HTTP 动词来描述操作,绝不单一的使用 POST 来处理所有逻辑。
HTTP 设计了很多动词,来表示不同的操作,RESTful 很好的利用的这一点,我们需要正确的使用 HTTP 动词,来表明我们要如何操作资源。
先来解释一个概念,幂等性
,指一次和多次请求某一个资源应该具有同样的副作用,也就是一次访问与多次访问,对这个资源带来的变化是相同的。
常用的动词及幂等性
动词 | 描述 | 是否幂等 |
---|---|---|
GET | 获取资源,单个或多个 | 是 |
POST | 创建资源 | 否 |
PUT | 更新资源,客户端提供完整的资源数据 | 是 |
PATCH | 更新资源,客户端提供部分的资源数据 | 否 |
DELETE | 删除资源 | 是 |
为什么 PUT 是幂等的而 PATCH 是非幂等的,因为 PUT 是根据客户端提供了完整的资源数据,客户端提交什么就替换什么,而 PATCH 有可能是根据客户端提供的参数,动态的计算出某个值,例如每次请求后资源的某个参数减 1,所以多次调用,资源会有不同的变化。
另外需要注意的是,GET 请求对于资源来说是不安全的,绝不 通过 GET 请求改变(更新或创建)资源。
真实使用中,为了方便统计类的数据,会有一些例外情况,例如帖子详情,记录访问次数,每调用一次,访问次数 +1。这种情况下可以考虑页面展示成功后,再次调用一个 POST 请求去更新阅读数。
4. 使用 HTTP 状态码进行通讯
必须利用 HTTP 状态码和客户端进行通讯。
有一些 API 的设计,不论接口的状态成功与否,都会返回 200 ,然后使用自定的状态码,例如说 :
{
// 数据不存在
error_code: 30404
}
这种方法是不可取的。
HTTP 状态码是行业标准,意味着成千上万开发者都在认同和使用这套规则,意味着他们写出来的 HTTP 通讯程序(类库)也在使用这套规则。所以没有必要,也不该重新发明自己的一套规则。
HTTP 提供了丰富的状态码供我们使用,正确的使用状态码可以让响应数据更具可读性。、
- 200 OK - 对成功的 GET、PUT、PATCH 或 DELETE 操作进行响应。也可以被用在不创建新资源的 POST 操作上
- 201 Created - 对创建新资源的 POST 操作进行响应。应该带着指向新资源地址的 Location 头
- 202 Accepted - 服务器接受了请求,但是还未处理,响应中应该包含相应的指示信息,告诉客户端该去哪里查询关于本次请求的信息
- 204 No Content - 对不会返回响应体的成功请求进行响应(比如 DELETE 请求)
- 304 Not Modified - HTTP 缓存 header 生效的时候用
- 400 Bad Request - 请求异常,比如请求中的 body 无法解析
- 401 Unauthorized - 没有进行认证或者认证非法
- 403 Forbidden - 服务器已经理解请求,但是拒绝执行它
- 404 Not Found - 请求一个不存在的资源
- 405 Method Not Allowed - 所请求的 HTTP 方法不允许当前认证用户访问
- 410 Gone - 表示当前请求的资源不再可用。当调用老版本 API 的时候很有用
- 415 Unsupported Media Type - 如果请求中的内容类型是错误的
- 422 Unprocessable Entity - 用来表示校验错误
- 429 Too Many Requests - 由于请求频次达到上限而被拒绝访问
强制 User-Agent
强制客户端在请求时,必须发送 User-Agent 信息。
User-Agent 信息包含两部分,客户端信息 + 版本,使用斜杆分隔:
User-Agent: Mixin Bot iOS/2.1.37
User-Agent: Mixin Bot Android/2.1.22
User-Agent: MixPay PHP SDK/2.1.22
User-Agent: MixPay GO SDK/2.1.22
API 后端接收到 User-Agent 数据后可以暂时不做处理,但是后续有特殊的业务需求时,可以针对某个客户端具体到版本,进行特殊的数据处理。
常见的使用场景,是废弃客户端:例如一个银行 APP,升级了交易时的加密算法,低于 5.0 版本的客户端因为安全原因,必须废弃。针对此情况,可通过后端 API 判断 User-Agent 标头,对低于 5.0 的版本的客户端请求,返回专属的数据,如 APP 首页的第一个 Banner 显示请升级客户端,安全升级无法使用的提示。
现实生产中,有些客户端用户会关闭系统的应用自动更新功能,多版本客户端是无法避免的问题。有了 User-Agent ,我们可以更加灵活的做针对性处理。
单数 or 复数?
资源路由路由 URI 必须 使用复数形式,如:
-
/photos/create
-
/photos/{photo}
错误的例子如:
/photo/create
/photo/{photo}
更多建议: