上周五下午,办公室里飘着咖啡香,我正美滋滋地准备收工下班,突然钉钉炸了。
「兄弟,你这接口返回的数据格式太随性了,我这边解析了三遍才搞对。」
「还有,你那个分页参数是pageSize还是page_size还是pagesize?三个地方三个写法,我查文档查到头秃。」
我默默放下背包,默默打开电脑,开始了新一轮的「接口优化」。
这不是我第一次被吐槽,大概率也不是最后一次。在踩了无数坑之后,我决定把这些年踩出来的经验整理一下,分享给各位还在API设计上「作」的同行。
坑一:把HTTP状态码当摆设
见过最离谱的接口:请求成功了,返回200;请求失败了,返回200;服务器挂了,还是返回200。
我问他为什么,他理直气壮:「业务逻辑上都处理了啊,返回200表示我收到请求了嘛。」
我当时差点把咖啡喷屏幕上。
HTTP状态码是干什么用的?是让调用方一眼看出请求结果用的。你返回一个200,结果body里写着{"code": 500, "msg": "服务器爆炸"},这不是脱了裤子放屁吗?
正确的做法:
// 成功
return response.status(200).json({ data: result });
// 客户端错误
return response.status(400).json({ error: "参数校验失败" });
// 服务端错误
return response.status(500).json({ error: "服务暂暂停,请稍后重试" });
约定好状态码语义,让错误处理变成一件幸福的事。
坑二:分页参数随心所欲
这是重灾区。每个团队都说自己在做RESTful,但分页参数的设计简直像在玩大家来找茬。
/api/users?page=1&size=20
/api/users?pageNo=1&pageSize=20
/api/users?offset=0&limit=20
/api/users?from=0&to=20
你能信?我见过一个项目里同时存在四种写法,分别来自四个不同的开发。
我的建议是:选择page+size或者offset+limit其中一种,然后死磕到底。最好在文档里写清楚,别让调用方去猜。
// 推荐:page + size(更直观,适合业务场景)
GET /api/users?page=1&size=20
// 也推荐:offset + limit(适合大数据量场景)
GET /api/users?offset=0&limit=20
返回结果里也要带上分页元数据,让调用方知道自己有多少页、当前第几页、总共多少条。
{
"data": [...],
"pagination": {
"page": 1,
"size": 20,
"total": 1523,
"totalPages": 77
}
}
坑三:命名全凭心情
见过最随性的API命名:
/api/getUserInfo
/api/Get_User
/api/query-user-data
/api/userInfoQuery
四种写法表示同一个功能,我都不知道该佩服还是该骂人。
RESTful的核心是「资源」和「操作」。资源用名词表示,操作用HTTP方法表示。
// 获取用户列表
GET /api/users
// 获取单个用户
GET /api/users/{id}
// 创建用户
POST /api/users
// 更新用户
PUT /api/users/{id}
// 删除用户
DELETE /api/users/{id}
统一命名规范:全小写,用连字符分隔单词。别学某些人用驼峰,别学某些人用下划线,别学某些人用混合大小写。
坑四:版本控制等于没控制
「这个接口还能用多久?」——这个问题应该由接口提供方主动回答,而不是等调用方来问。
版本控制不是可选的,是必须的。
// 放在URL路径里(最常见)
GET /api/v1/users
GET /api/v2/users
// 放在header里(比较少见)
GET /api/users
API-Version: 2023-01-01
每个版本要有明确的生命周期:什么时候弃用、什么时候下线。提前通知调用方,给他们足够的迁移时间。
坑五:错误信息像谜语
线上出过一起事故:接口返回「操作失败」,没有任何详细说明。调用方以为是自己参数问题,查了两天,最后发现是服务器硬盘满了。
错误信息要包含足够的信息量,但不要包含敏感信息。
// 反面教材
{ "error": "操作失败" }
// 正确示范
{
"error": {
"code": "VALIDATION_ERROR",
"message": "请求参数校验失败",
"details": [
{ "field": "email", "message": "邮箱格式不正确" },
{ "field": "phone", "message": "手机号不能为空" }
],
"requestId": "req_abc123"
}
}
加上requestId,这样出问题了可以快速定位日志。
坑六:不写文档或者文档是摆设
最怕的不是接口写得烂,是接口写得还行但文档像垃圾。
我见过最离谱的文档:接口说明写着「获取用户信息」,没有参数说明,没有返回示例,没有错误码说明。点开评论,有人问「这个接口返回的是什么」,作者回复「你看代码就知道了」。
代码即文档这种话,在API场景下就是耍流氓。
文档要包含:
- 接口功能说明
- 请求参数(含类型、是否必填)
- 返回数据结构(含每个字段含义)
- 错误码说明
- 调用示例
现在有很多工具可以自动生成文档:Swagger、Apifox、Postman。至少把文档和代码同步更新这件事,做起来没那么难。
坑七:安全意识约等于零
见过有人在URL里传密码的:「/api/login?username=admin&password=123456」。也见过把所有接口都设计成无需认证的,美其名曰「方便调试」。
方便调试是方便了,被人攻击的时候哭都来不及。
基本要求:
- 敏感信息走POST body,不要放URL参数里
- 使用HTTPS
- 做好接口限流,防止DDoS
- 关键接口加上幂等性设计
- 日志里不要记录敏感信息(密码、token等)
说在最后
写接口这件事,入门门槛很低,但做好很难。一个烂接口会影响一堆人,一个好接口会让调用方发出「真香」的感叹。
下次写接口之前,先问问自己:如果别人调用这个接口,最可能在哪里卡住?如果我是个完全不知道业务的人,看文档能看懂吗?
好的API设计就像好的代码注释——不是为了炫耀,而是为了让他人(包括未来的自己)能少踩坑。
共勉。