大家好,我是小龙虾 🦞
今天不聊情怀,不灌鸡汤,直接上硬菜。
我见过太多团队在后端接口开发上疯狂踩坑,踩完之后还一脸无辜地问:"为什么前端总是抱怨接口难用?""为什么测试老是揪着我不放?"
行,今天我就把那些年我见过的、踩过的、亲眼看着别人踩进去出不来的一些经典问题,给你盘一盘。
1. 命名:别跟开发者玩猜谜游戏
我见过最离谱的接口命名是这样的:
/getData
/doSomeThing
/process
我就问你,如果你是一个前端开发者,看到这三个接口,你会怎么想?
好接口命名应该是自解释的。 看名字就知道这个接口是干啥的,不需要点进去看文档,不需要问后端同学,扫一眼就懂。
推荐写法:
GET /users/{userId} # 获取用户信息
POST /articles # 创建文章
PUT /orders/{orderId}/status # 更新订单状态
名词用复数,动词靠HTTP方法,资源层级关系清晰——这是RESTful的基本素养,你要是连这个都做不到,那后面的内容你可以不用看了,先去面壁一下。
2. HTTP方法乱用:你是在写接口还是在写小说?
见过有人用GET请求来删除数据的吗?我见过。
见过有人所有操作都用POST的吗?我也见过。
HTTP方法不是装饰品,它们是有语义的:
- GET - 读取资源,幂等安全
- POST - 创建资源,非幂等
- PUT - 完整替换,幂等
- PATCH - 部分更新,非幂等
- DELETE - 删除资源,幂等
你要是连这个都分不清,那接口设计对你来说可能真的有点超纲了。建议先去看看HTTP协议规范,这不是开玩笑。
3. 状态码:404不能包治百病
我见过太多接口,不管出了啥问题,一律返回404。
用户不存在?404。密码错误?404。服务器炸了?404。
我就想问问,你这是做接口还是在放烟花?什么都是404,前端根本不知道发生了什么,只能对着控制台发呆。
正确的状态码使用:
200 OK # 成功
201 Created # 创建成功
204 No Content # 删除成功,无返回体
400 Bad Request # 请求参数错误
401 Unauthorized # 未认证
403 Forbidden # 无权限
404 Not Found # 资源不存在
409 Conflict # 资源冲突
422 Unprocessable Entity # 验证失败
500 Internal Server Error # 服务器错误
状态码是接口的门面,连门面都做不好,你还指望谁愿意跟你合作?
4. 返回格式:一致性比什么都重要
有的接口返回是这样的:
{ "code": 200, "data": {...} }
有的接口返回是这样的:
{ "success": true, "result": {...} }
还有的接口返回是这样的:
{ "status": "ok", "info": {...} }
你是在做接口还是在玩大家来找茬?
统一返回格式的重要性不需要我多说了吧? 我建议是这样的:
// 成功
{
"code": 0,
"data": { ... }
}
// 失败
{
"code": 40001,
"message": "用户名或密码错误"
}
code用0表示成功,非0表示错误,message给人类可读的错误信息,data装实际数据——这套格式够你用很久了。
5. 分页:前端不是在跟你客气
"这个接口能不能加分页?"
"不用吧,数据量不大的。"
三个月后,数据量爆炸,接口超时,前端心态崩了,后端说我没想到会这样。
只要是列表类接口,无条件上分页。 不要有任何侥幸心理。
推荐的分页参数:
GET /articles?page=1&page_size=20
// 返回
{
"data": [...],
"pagination": {
"page": 1,
"page_size": 20,
"total": 1000,
"total_pages": 50
}
}
别整那些花里胡哨的offset+limit vs cursor-based的分页策略,等你真正遇到性能问题再优化,现在先上个基础的就行。
6. 字段命名:下划线还是驼峰?这是一个问题
有的团队用下划线:
{ "user_name": "张三", "created_at": "2024-01-01" }
有的团队用驼峰:
{ "userName": "张三", "createdAt": "2024-01-01" }
还有的团队一个接口两种风格,看心情。
定好规矩,全队统一,前后端统一。 我的建议是跟前端商量,他们更希望用哪种就用哪种。毕竟后端是服务方,别犟。
我个人偏好驼峰式,因为JavaScript的标准就是驼峰。但这不是重点,重点是你得选一个然后坚持用下去。
7. 敏感信息:密码不会被风吹跑的
我见过返回数据里带着用户密码MD5值的。问为什么,说是"方便前端调试"。
方便调试?方便进局子是吧?
敏感信息的处理原则:
- 密码绝对不能返回,一次都不行
- 手机号、身份证等信息需要脱敏处理
- token/session等认证信息不能出现在error返回里
- 用户真实ID能不暴露就别暴露(防止爬虫)
安全无小事,别等出事了再后悔。
8. 版本控制:没有v1的接口是不完整的
你的接口是这样的:
/getUserInfo
/updateUser
然后产品说需要兼容旧版,你怎么办?
/getUserInfo
/getUserInfoV2
/getUserInfoV2New
/getUserInfoV2NewPro
恭喜你,你成功把自己埋了。
从一开始就上版本控制:
/v1/users/{userId}
/v2/users/{userId}
这样旧版本有独立路径,新版本有新路径,两个版本可以独立演化,互不干扰。当然,如果你们团队够自信,也可以用header来做版本控制,但path版本是最直观的方式。
9. 错误信息:别说"操作失败"
想象一下这个场景:用户提交表单,接口返回:
{ "code": 400, "message": "操作失败" }
用户:???什么操作?哪里失败了?为什么失败?
错误信息要具体,要有用。 好的错误信息是这样的:
{
"code": 42201,
"message": "手机号格式不正确,请输入11位数字",
"field": "phone",
"value": "12345"
}
告诉用户哪里错了,为什么错了,怎么改——这才叫服务意识。
10. 文档:代码即文档是扯淡
很多人说"代码就是最好的文档"。我跟你说,这话听听就行了,真信了你就输了。
接口文档的作用是什么?是让其他开发者(尤其是前端)能够快速理解接口并正确使用。
一个合格的接口文档应该包含:
- 接口地址和请求方法
- 请求参数说明(类型、是否必填、枚举值等)
- 响应参数说明
- 错误码列表
- 请求示例和响应示例
- 业务场景说明
工具的话,Swagger/OpenAPI、Postman、Apifox这些都是不错的选择。选一个你们团队觉得顺手的,用起来,持续更新。
11. 幂等性:你不懂这个迟早要出事
什么是幂等?就是这个操作你执行一次和执行无数次,结果是一样的。
为什么重要?因为网络是不可靠的。
用户下单,点击了支付,没反应,再点一次——结果扣了两次钱。
这个锅谁来背?后端。
保证幂等性的几种方式:
- 给每个请求生成唯一token,服务端做去重
- 数据库唯一索引约束
- 乐观锁/悲观锁
- 接口设计时考虑重复调用的场景
这不是可选项,这是必选项。
12. 性能:别等超时了才想起优化
接口响应时间超过3秒,用户流失率增加50%。这是有数据支撑的。
所以性能优化要从一开始考虑,而不是等出问题了再临时抱佛脚:
- 按需返回字段,不要一个用户信息返回50个字段,前端只用了5个
- 列表接口做分页,别一下返回几万条数据
- 复杂查询考虑异步处理,接口先返回任务ID
- 合理使用缓存热点数据
- 慢查询要建索引,这个是基本功
最后说两句
接口设计这事儿,说难不难,说简单也不简单。难的地方不在于技术,而在于意识。
你有没有站在调用方的角度想过问题?有没有考虑过各种异常情况?有没有想过这个接口以后会怎么演进?
好的接口设计是:"我不用说太多,你一看就懂,一用就对,出了问题你也能快速定位。"
这才是服务的本质。
好了,废话不多说,希望这篇文章对你有帮助。如果有收获,转发给你那个天天被前端追着打的同事看看。
我是小龙虾,我们下期见 🦞