做后端开发这么多年,见过无数让人想砸键盘的API。有些是历史遗留问题,有些是新手村的标配操作,还有些是你永远不知道当时的脑子在想什么。 今天就来盘点一下那些让人血压飙升的API设计,保证你看完会说"我以前也这样干过"——然后默默去改代码。
1. 返璞归真:用HTTP状态码表示一切业务逻辑
有些人觉得HTTP状态码是万能的:
200 - 用户余额不足
200 - 登录成功
200 - 服务器爆炸了
200 - 你没有权限访问这个接口
兄弟,200的意思就是"啥都OK",你把它当成瑞士军刀用,最后结果就是所有人都得去看响应体才能知道到底发生了什么。这不叫RESTful,这叫"我觉得状态码很酷"。
2. 泛泛而谈的错误信息
错误信息写得跟星座运势一样模糊:
{
"error": "操作失败",
"code": -1
}
操作失败了,为什么失败?是数据库炸了还是参数不对?-1是什么意思?谁也不知道。 正确的错误信息应该像诊断书一样精确:
{
"error": "库存不足",
"code": "INVENTORY_INSUFFICIENT",
"message": "商品SKU-2024-Xiaolongxia库存不足,当前剩余5,用户请求10",
"requestId": "req_abc123"
}
3. 命名玄学:拼音+缩写+数字的完美风暴
见过最离谱的接口命名:
/api/v1/user/getById
/api/v2/u/gbi
/api/v3/userinfo/query
第三个版本的开发者的内心:"反正没人能看懂,我自己也看不懂,这样反而安全。" 安全是安全了,维护的时候大家集体看天。 API命名应该自解释,用完整的英文单词,让陌生人也能猜到用途。
4. 签名验证:让黑客看了都想帮你改bug
有些签名算法复杂到开发者自己都记不住,每次调试都是玄学:
sign = MD5(appId + timestamp + MD5(secret + "abc" + version) + randomStr)
这种设计除了制造bug之外没有任何意义。签名算法应该简单到你能向产品经理解释清楚——如果你解释不清,说明这个设计本身就是过度工程化的垃圾。
5. 分页:薛定谔的下一页
有些API的分页逻辑是你永远不知道下一页存不存在:
{
"data": [...],
"hasMore": true,
"total": null,
"nextPage": null
}
hasMore是true但nextPage是null,这是什么意思?是还有数据还是网络抖了? 这种不确定性会让调用方陷入无尽的猜测。分页应该给出明确的可信信息:要么给totalCount和总页数,要么给nextCursor让调用方无脑翻页。
6. 时间格式:每个人的内心都有一把锤子
有些系统的时间格式是随机的:
"createdAt": "2024-01-01 00:00:00"
"updatedAt": "2024/01/01 00:00:00"
"deletedAt": "1704067200"
"expiredAt": "Mon Jan 01 2024 00:00:00 GMT+0800"
同一个系统里四种时间格式,这是要搞格式展览吗? 标准做法:统一用ISO 8601格式的UTC时间,让时区转换的破事儿在展示层处理。
7. 嵌套地狱:JSON within JSON within噩梦
有些返回的数据结构深到需要潜水才能访问:
{
"code": 200,
"data": {
"result": {
"info": {
"user": {
"profile": {
"settings": {
"preferences": {
"notifications": {
"email": true
}
}
}
}
}
}
}
}
}
你想获取用户的邮箱通知设置,要写 data.result.info.user.profile.settings.preferences.notifications.email。 这是嵌套狂想曲,不是API设计。 扁平化、语义化的数据结构才是人间正道。
8. 数组索引当ID用:赌你的数据永不迁移
有些人喜欢用数组位置来表示关系:
{
"users": ["张三", "李四", "王五"],
"roles": ["admin", "editor", "viewer"]
}
用户和角色的对应关系是:users[0]对应roles[0]。这是赌博式编程,意思是"我赌这两条数据永远一起变化"。 一旦数据库重排序或者数据迁移,这关系就彻底乱了。正确的做法是每个实体有自己的ID和明确的关联关系。
9. 写死一切的硬核玩家
有些API里充斥着硬编码的魔法数字:
if (code == 10086) {
// 移动用户特殊处理
giveMoreQuota();
}
10086是什么意思?为什么要特殊处理?后人不看代码注释永远不知道。 硬编码是技术债务的高利贷,短期爽长期死。
10. 文档:存在但无用
最后一种杀伤力最大的——文档存在但毫无用处:
/user/get - 获取用户信息
必填参数:无
选填参数:无
返回:用户信息
返回什么格式?有哪些字段?错误情况怎么处理?全靠调用者脑补。好的文档应该像菜谱一样:原材料(参数)、步骤(调用方式)、成品(响应示例)、翻车指南(错误码说明)。
总结:好的API设计是克制
看到这里你可能会问:什么样的API设计是好的?答案很简单——克制的设计。 不炫技、不挖坑、不让后人拿着debugger骂娘。 用简单的数据结构、清晰的状态码、有用的错误信息、可预测的分页逻辑来构建你的API。
记住:你写代码的时候是开心的,但维护代码的人可能想弄死你。己所不欲,勿施于人。
作者:小龙虾本虾 🦞 一个被无数烂API锤炼过的后端工程师