做后端开发这些年,我见过太多API设计得让人想骂娘的代码。有些接口返回的数据结构跟盲盒一样,你永远不知道下一个字段是什么;有些接口文档写得跟小说似的,实际调用起来发现全是坑。今天我就把这些年踩过的坑整理出来,保证每一条都是真实案例,看完保证让你有种「这不就是我写的吗」的共鸣。
教训一:返回结构混乱,同一个接口两种脸
先说个我亲眼见过的骚操作。有个兄弟写的用户查询接口,成功返回用户信息,失败返回 {"error": "用户不存在"}。好家伙,成功是对象,失败是字符串,你要我在前端做类型判断吗?
正确的做法是统一返回结构:
{
"code": 200,
"message": "success",
"data": {
"id": 123,
"name": "张三"
}
}
// 错误时也返回同样的结构
{
"code": 404,
"message": "用户不存在",
"data": null
}
记住:一个接口,永远只有一种返回结构。不管是200还是500,返回的JSON顶层key必须一致。前端处理起来爽,大家都是打工的,何必互相为难。
教训二:分页参数随心所欲,页码从0还是1开始全凭心情
这个问题简直是后端界的月经题。offset=0 还是 offset=1?page=1 还是 page=0?limit 默认多少?
我建议你用这套方案:
- 使用
offset和limit,而不是page和page_size - offset 从 0 开始(程序员都懂)
- limit 默认值 20,上限 100(防手滑)
- 返回时带上总数和总页数
完整分页返回应该是这样:
{
"code": 200,
"data": {
"list": [...],
"pagination": {
"total": 1000,
"page": 1,
"page_size": 20,
"total_pages": 50
}
}
}
最最重要的是,文档里要写清楚!不要让调用方猜。
教训三:HTTP状态码乱用,200表示失败400表示成功
这个我真的遇到过。有一个接口,逻辑是「创建订单成功返回200,库存不足返回400」。我看到400状态码时第一反应是「我参数写错了?」,结果看了半天文档才发现,400在这里的意思是「业务逻辑上的失败」。
HTTP状态码是有规范的,不是你想怎么用就怎么用:
- 200:请求成功,服务端处理正常
- 400:客户端参数错误,请求格式不对
- 401:未认证,请先登录
- 403:已认证但没权限
- 404:资源不存在
- 500:服务端内部错误
如果你的「库存不足」是一种业务层面的失败,而不是用户输入的问题,请用200+业务错误码来处理。
教训四:接口版本管理?不存在的
很多小团队觉得「我们用户少,不需要做版本管理」。然后某天你改了一个字段,发现线上十几家客户全部炸了。
API版本管理是必须要做的,推荐的做法:
https://api.example.com/v1/users
https://api.example.com/v2/users
每个版本至少维护一年,老版本给足够的迁移时间。如果不想在URL里放版本号,至少在Header里处理:
Accept: application/vnd.example.v2+json
教训五:字段命名随心所欲,user_name 和 userName 和 userNameCN 同时存在
这个问题在团队协作时特别明显。有人说用下划线(user_name),有人觉得驼峰更优雅(userName),最后出来的接口四不像。
选一种规范,全团队统一。我的建议:
- 对外API用驼峰(JSON标准)
- 数据库字段用下划线(数据库惯例)
- 文档里明确说明
最怕的是同一个项目里混用,调用方要看两遍文档才能确定字段名,效率低下还容易出错。
教训六:接口文档过时了,以为在维护其实早已放弃
我见过最离谱的是一个团队的API文档,上面的「获取用户信息」接口返回字段有20多个,实际接口只返回8个。我问他们为什么,他们说「那个文档是两年前写的,后来删了一些字段但文档没更新」。
解决方案:
- 使用 Swagger/OpenAPI 规范编写文档,代码即文档
- 文档和代码必须同版本管理,改代码必须改文档
- 考虑用 API Mock 工具,让调用方能直接测试
一个好的文档应该包含:请求参数、返回字段、错误码示例、调用频率限制。如果你的文档三行字就结束了,那不叫文档,叫简历。
教训七:没有考虑向后兼容,删字段跟删好友一样随意
你永远不知道调用方在用你哪个字段。有个真实的教训:某产品觉得 nickname 字段没人用就删了,结果有客户的App直接读取这个字段展示用户名,删完之后那家客户的用户全部显示「匿名用户」。
删字段的正确姿势:
- 先标记为 deprecated,保留返回但提示「即将废弃」
- 等至少一个版本周期后再删除
- 删除前发邮件/公告通知所有集成方
- 宁可返回null也不能直接删字段
记住:你对API的任何改动都是重大决定,不是你一个人的事,是所有集成方的事。
写在最后
API设计这件事,说难听点就是「细节决定成败,习惯决定细节」。很多人觉得接口能跑就行,但真正考验功力的恰恰是这些看似无关紧要的设计选择。
好的API设计有三个标准:
- 一致性:所有接口遵循同一套规范
- 可预测性:调用方能猜到你的返回
- 文档完整性:文档和代码一样重要
下次写接口之前,先问自己三个问题:这个接口给谁用?对方看到返回会骂我吗?这个接口三个月后我还能看懂吗?
如果三个问题都能回答好,那你的接口至少不会被人追着骂了。至于能不能被人夸,那就需要更多的实战经验了——比如今天这篇文章没讲到的「RESTful到底怎么用」「GraphQL值不值得上」这些话题,留着下次再聊。
祝大家的API都被夸,不被骂。