为什么你的API总被吐槽?聊聊那些让人想砸键盘的设计
开门见山,我见过太多「能跑就行」的API了。不是我夸张,有些API的设计真的让人怀疑开发者是不是和用户有仇。
今天不整虚的,就聊聊那些年我们踩过的API设计坑,顺便给点靠谱的解决方案。你要是正在写API或者维护API,这篇绝对有用。
一、返回结构:别让客户端去猜谜
先问个问题:你的API成功返回什么?失败返回什么?error字段怎么设计的?
我见过最离谱的API是这样的:
{
"status": 200,
"data": {...},
"message": "操作成功"
}
好,看起来还行对吧?但你试试查个不存在的ID:
{
"status": 404,
"data": null,
"message": "未找到该记录"
}
404是HTTP状态码,但这是一个查询接口啊,查询「未找到」不等于资源不存在啊亲!更可怕的是,前端工程师看到这个200状态码就以为成功了,然后一顿操作猛如虎去访问null里面的字段——恭喜你,喜提线上bug一枚。
我的建议是:HTTP状态码只管通讯层,业务层状态独立设计。
{
"code": 0,
"message": "成功",
"data": {...}
}
{
"code": 40401,
"message": "用户不存在或已注销",
"data": null
}
这样前端只需要判断code是否为0,剩下的交给前端自己根据code做业务分流。你好我好大家好。
二、分页:这是一个哲学问题
分页参数怎么设计?offset还是cursor?page还是size?
常见的几种方案:
- offset/page方案:?page=1&size=20,简单但有坑——删除数据后分页会错位
- offset方案:?offset=0&limit=20,经典但深分页慢
- cursor方案:?cursor=xxxxx&size=20,推荐,性能稳定
cursor分页是性能党和数据一致性要求高的场景首选。为啥?你翻到第100页,这时候第50页有人删了一条数据,offset方案的用户就懵了——怎么第50页突然多了一条?
cursor基于位置标识,不存在这个问题。当然cursor也有代价:不能跳页。所以我的建议是:
- 列表数据需要跳页的,用offset
- 无限滚动、推荐流这种场景,用cursor
三、命名:代码即文档
API的参数名和返回值名是你给前端兄弟写的文档。
我见过这样的接口:
POST /api/user/update
Body: {
"nm": "张三",
"ag": 18,
"gen": 1
}
你是不是觉得自己很潮?缩写很酷?等到出bug需要排查的时候,或者新来的同事看这段代码,你就知道什么叫技术债了。
命名原则就一条:言出必达,不需要解释。
POST /api/users/{user_id}
Body: {
"name": "张三",
"age": 18,
"gender": "male"
}
顺便说一句,URL路径用复数名词是个约定俗成的规矩。/users比/user更符合REST风格,也更一致——你查一个用户是GET /users/{id},查一群用户是GET /users,不需要改变名词。
四、版本管理:沉默的陷阱
你的API v1还在跑吗?v2出来了吗?v1打算什么时候杀?
很多人觉得版本管理是小事,等业务跑起来了才发现:完了,v1的几百个调用方根本不知道要不要迁移,更不敢动。
我的版本策略是这样的:
- URL路径版本:/api/v1/, /api/v2/,最直观但迁移成本高
- Header版本:Accept: application/vnd.api.v2+json,优雅但隐蔽
- Query版本:/api?version=2,门槛最低但不REST
我的建议是:小团队用URL版本,大团队用Header版本。URL版本一目了然,谁在用清清楚楚。Header版本适合多端适配和微服务内部调用。
更重要的是——每个版本都要有明确的废弃时间线。我一般这么定:
- 新版本发布后,旧版本保守运行6-12个月
- 提前3个月发公告,告知迁移指南
- 废弃后保留1个月返回deprecation警告
五、错误信息:给开发者一条活路
想象一下这个场景:用户下单失败了,API返回「下单失败」。然后呢?前端工程师对着这个错误信息发呆,不知道到底是库存没了、余额不足、还是系统抽风。
错误信息设计原则:给够信息,但不要给废话。
{
"code": "INVENTORY_SHORTAGE",
"message": "库存不足",
"details": {
"requested": 5,
"available": 2,
"sku_id": "SKU20250517"
},
"support_url": "https://help.comck.com/error/inventory"
}
这个错误信息包含了:
- 机器可读的错误码(前端可以据此展示不同的UI)
- 人类可读的错误描述
- 调试所需的上下文信息
- 帮助文档链接(减少客服压力)
写在最后
API设计这件事,说难不难,说简单也不简单。核心就一句话:把使用者当人看。
你想啊,调用你API的人可能是个刚入行的小前端,可能是个半夜被拉起来处理线上故障的哥们,也可能是一个完全不认识你的第三方开发者。你设计的每一处便利,都是在给他们续命。
下次写API之前,停下来问自己三个问题:
- 这个名字我自己第一眼能看懂吗?
- 出错了,调用方能自己定位问题吗?
- 这个设计,6个月后我自己维护还骂娘吗?
如果三个都是yes,那你这API至少不会被人挂到吐槽社区。做到了这点,你就已经赢了70%的开发者了。
行了,就聊到这儿。有收获的点个赞,没收获的……算了,那是你审美问题不是我技术问题。🙃