做后端开发这么多年,见过太多奇葩的 API设计了。有的 API 看得我怀疑人生,有的设计让人忍不住想问开发者:你写这玩意儿的时候到底在想什么?
今天就来聊聊那些年我们踩过的 API 设计坑,以及怎么优雅地避开它们。
一、URL 设计:别把 API 写成句子
最常见的反面教材就是把所有操作都塞进 URL 里,像这样:
POST /api/getUserInfo
POST /api/deleteUserById
POST /api/updateUserPassword
GET /api/fetchAllOrdersForUser
我的天,请问你是把 API 当英语句子写吗?
正确的姿势:
GET /api/users/{id}
DELETE /api/users/{id}
PATCH /api/users/{id}/password
GET /api/users/{id}/orders
记住 HTTP 方法就是动词,URL 名词才是主角。一个好的 RESTful API 应该让你的 URL 看起来像「资源」而不是「动作」。
二、状态码:别什么都是 200 OK
有些人特别爱返回 200,不分青红皂白:
// 登录失败也返回 200?
{ "code": 401, "message": "用户名或密码错误", "data": null }
// 找不到资源也是 200?
{ "code": 404, "message": "用户不存在", "data": null }
这不是逗我玩吗?HTTP 状态码之所以存在,就是为了让你不用看 body 都知道请求大概什么情况。结果你全给我返回 200,那我还得解析 body 才能知道到底成没成?
建议:
- 200 - 成功
- 201 - 创建成功(比如 POST 新建资源)
- 400 - 请求参数错误
- 401 - 未认证(没登录)
- 403 - 没权限
- 404 - 资源不存在
- 500 - 服务器抽风
当然,业务层面的错误(比如「余额不足」)可以用业务错误码,但 HTTP 状态码这种基础设施,能用就用,别偷懒。
三、分页:没有分页的列表就是定时炸弹
「给我查一下所有用户」——这种需求谁提的?出来,我保证不打你。
任何列表接口都必须支持分页,不接受反驳。可能你的用户现在只有 100 条数据,一次性返回没问题,等用户量起来了,10000 条数据一次性返回,数据库炸了、前端渲染卡死了、网络请求超时了——恭喜你,成功制造了一个生产事故。
推荐的分页实现:
GET /api/users?page=1&size=20
// 返回结构
{
"data": [...],
"pagination": {
"page": 1,
"size": 20,
"total": 1000,
"totalPages": 50
}
}
简单明了,要第几页、每页多少条、清不清楚总共有多少数据——一目了然。
四、版本号:早加早享受
很多同学信誓旦旦地说「我这 API 绝对不需要版本号」,结果上线三个月就开始打脸——业务逻辑变了,旧的客户端还在用,总不能让人强制升级吧?
版本号三种常见写法:
// 1. URL 路径(最推荐)
GET /api/v1/users
GET /api/v2/users
// 2. Header
Accept: application/vnd.myapp.v1+json
// 3. Query 参数(不推荐)
GET /api/users?version=1
第一种最直观,版本升级改个路径就行,老接口该下线的下线,该维护的维护,泾渭分明。
五、命名:保持一致性比你想的重要
有些项目的 API 命名堪称「风格灾难」:
GET /api/get_user_info // 驼峰
GET /api/user-info // 中划线
GET /api/user_info // 下划线
POST /api/CreateUser // 还有大写?
DELETE /api/delUser // 缩写也不统一
这种 API 用起来,前端同学的心态大概是这样的:我是谁?我在哪?我到底该用哪个?
我的建议:
- URL 统一用小写 + 中划线(kebab-case):
/api/user-info - JSON 字段统一用小写 + 下划线(snake_case)或驼峰(camelCase),看团队习惯
- 不要混用,选定一种风格就贯穿整个项目
六、错误信息:给开发者留条活路
最气的错误返回是这样的:
{
"error": "操作失败"
}
{
"message": "系统错误"
}
大哥,我知道了操作失败,那到底是啥问题?参数错了?权限没了?数据库炸了?你倒是说啊!
好的错误返回应该长这样:
{
"error": {
"code": "INVALID_PARAMETER",
"message": "请求参数校验失败",
"details": [
{
"field": "email",
"message": "邮箱格式不正确"
},
{
"field": "password",
"message": "密码长度不能少于 8 位"
}
]
}
}
这样前端直接可以渲染出具体的错误提示,用户知道自己错在哪,开发者也知道问题出在哪,皆大欢喜。
写在最后
API 设计这事儿,说难不难,说简单也不简单。难的是一致性——不是做对一次就行,而是次次都对,整个团队都对。简单的是——只要记住几个原则,就能避开 80% 的坑。
最后送大家一句话:写 API 的时候想象一下别人怎么用,你是想让人家用得舒服,还是想让人家骂娘?将心比心,很多问题就有答案了。
祝大家的 API 都写得漂漂亮亮的,前端用得开开心心的,线上跑得稳稳当当的。