RESTful API 设计翻车现场:那些年我们踩过的坑
做后端开发这么多年,我发现一个有趣的现象:写 API 的时候,每个人都觉得自己的设计很优雅。但半年后回看代码,往往只想穿越回去掐死当时的自己。今天来聊聊我见过的那些 API 设计翻车现场,保证你看完会说"这不就是我吗"。
一、HTTP 状态码:别再用 200 塞一切了
我见过最离谱的 API 是这样的:
// 不管成功还是失败,统统返回 200
{
"code": 0, // 0表示成功,其他表示失败
"message": "操作成功",
"data": null
}
兄弟,你这是在 HTTP 协议上又封装了一层协议啊!那 HTTP 状态码是用来干嘛的?装饰用的吗?
更可怕的是,这种设计会导致你的接口文档变得像悬疑小说:
// 这个接口可能返回 200 但实际出错了
// 需要看 code 字段才知道真相
// 你是在玩大家来找茬吗?
正确姿势:
// 成功
200 OK
{"id": 123, "name": "小龙虾"}
// 客户端错误
400 Bad Request
{"error": "validation_failed", "message": "手机号格式不对"}
401 Unauthorized
{"error": "unauthorized", "message": "token过期了"}
404 Not Found
{"error": "not_found", "message": "这个用户不存在"}
500 Internal Server Error
{"error": "internal_error", "message": "服务器抽风了"}
二、命名:你的接口名是认真的吗?
我见过最离谱的接口命名包括:
// 方法名是 getById,但实际是删除操作
POST /api/user/getById // 这是删除!
// 功能重复的两个接口
POST /api/user/create
POST /api/user/add // 有什么区别??
// 中英混杂
GET /api/user/getUserInfoById // 你是怕程序员看不懂吗
RESTful 的核心是"所见即所得":
// 资源 + 操作 = 接口名
GET /users // 获取用户列表
GET /users/123 // 获取单个用户
POST /users // 创建用户
PUT /users/123 // 更新用户(完整)
PATCH /users/123 // 更新用户(部分)
DELETE /users/123 // 删除用户
三、分页:这是一个哲学问题
翻车现场一:
// 返回所有数据,让前端自己截
GET /api/users
// 返回: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...无限延长]
这接口一旦用户多了,就是一场 DDoS 攻击。只不过攻击来源是你自己的用户。
翻车现场二:
// offset 太大直接爆炸
GET /api/users?offset=1000000&limit=10
// 数据库: 全表扫描...
正确的分页方式:
// 1. 游标分页(性能好)
GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=10
// 返回:
{
"data": [...],
"next_cursor": "eyJpZCI6MTMzfQ",
"has_more": true
}
// 2. 如果数据量确实小,用 offset 也行,但记得加 limit 上限
GET /api/users?offset=0&limit=20 // limit 最大 100
四、版本管理:没有版本的 API 是不负责任的 API
我见过最勇敢的开发者是这样设计的:
// 不管了,硬上!
GET /api/users
// 半年后...
// 需要加 v2 功能了
// 怎么办?
// 创建新接口!
GET /api/usersV2
// 又过了半年...
// 有了 usersV2,为什么不用?来个大杂烩
GET /api/usersV3 // 包含 V1 的核心 + V2 的功能 + 一些新东西
// 维护者:我是谁?我在哪?这个接口是干嘛的?
版本管理的正确打开方式:
// 路径版本(最直观)
GET /api/v1/users
GET /api/v2/users
// Header 版本
GET /api/users
Accept: application/vnd.api.v2+json
// 建议用路径版本,前端同学会感谢你的
五、错误信息:别只告诉前端"错了",要说清楚哪错了
最常见的错误返回:
// 这样写等于没写
{
"message": "操作失败"
}
// 前端:哪里失败了?为什么失败了?我该怎么办?
// 后端:我也不知道,反正就是失败了
好的错误信息应该像这样:
{
"error": "validation_error",
"message": "参数校验失败",
"details": [
{
"field": "email",
"message": "邮箱格式不正确",
"value": "this_is_not_email"
},
{
"field": "age",
"message": "年龄必须大于0",
"value": -5
}
],
"request_id": "req_abc123" // 用于排查问题的请求ID
}
六、写在最后
API 设计这件事,说难听点就是"一次写错,半年买单"。每次你偷懒用模糊的命名、随意的状态码、敷衍的错误信息,都是在给未来的自己和同事挖坑。
好的 API 设计其实就一个原则:让你的接口像一篇说明文,清清楚楚,明明白白。调用者不需要猜,不需要看源码,不需要问你"这个接口到底是怎么用的"。
记住:你现在写的代码,就是半年后你骂的对象。省点心,好好写。🙏
P.S. 如果你觉得这篇文章有用,说明你身边可能有个正在写烂 API 的同事。转发给他,救人一命,胜造七级浮屠。