API设计翻车现场:10个让我后悔莫及的蠢设计

2026-06-12 7 0

做后端开发这么多年,我踩过的API设计坑比你吃过的盐还多。今天不整那些冠冕堂皇的"最佳实践",就聊聊真实翻车经历,保证你看完会说"卧槽这个我也踩过"。

1. 把POST和GET当双胞胎用

最早的我是这么想的:GET不安全?那就用POST呗,反正都是发请求。结果我的API是这个画风:

POST /api/deleteUser?id=123
GET /api/createOrder
POST /api/getUserInfo

当时还觉得自己很机智。后来才知道,HTTP语义不是摆设。GET是幂等的(敲一万次结果一样),POST是不幂等的。你把删除做成POST,调用方手抖一下就删库跑路?这种设计要么是蠢要么是坏。

正确姿势:GET就是读,POST就是写,DELETE就是删。RESTful不是束缚,是护身符。


2. 状态码随便填,200打天下

你们有没有见过这种API?不管发生啥,统统返回200,然后code字段写个1表示成功,0表示失败:

{
  "code": 0,
  "message": "用户不存在",
  "data": null
}

我就问一句,既然用户不存在,为啥HTTP状态码不是404?既然你接口返回了错误,凭啥还给我200?这种设计让监控和错误处理全变成摆设。网关看到200,以为天下太平,实际上后端已经炸了。

正确姿势:用对HTTP状态码。404就是找不到,400就是请求参数有问题,401就是没登录,403就是没权限,500就是后端抽风。别懒,状态码本身就是接口的一部分。


3. 蛇形命名和驼峰命名混用

有一天前端同事拿着日志来找我:"后端大哥,你这接口一会儿snake_case一会儿camelCase,我字段映射写得心态崩了"。我一看,好家伙,我自己都没发现:

{
  "user_name": "张三",       // 数据库用的下划线
  "userAge": 25,             // 某次赶工用的驼峰
  "created_at": "2024-01-01", // 时间戳又是下划线
  "loginTime": "2024-01-02"   // 这个又是驼峰
}

这种不一致性会传染。一个项目里有人用user_name,有人用userName,最后就是灾难。

正确姿势:定好命名规范,全团队遵守。前端JavaScript用camelCase,Python后端用snake_case,数据库用下划线。接口层做好转换,别让调用方来适配你。


4. 分页参数随心所欲

我的第一版分页是这样的:

GET /api/users?page=1
GET /api/orders?offset=0&limit=20
GET /api/products?from=0&size=10

三个接口三种分页方式,前端同学每次都要写三套解析逻辑。后来我学乖了,统一成:

GET /api/users?page=1&page_size=20

而且返回结构也统一:

{
  "data": [...],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total": 1000,
    "total_pages": 50
  }
}

正确姿势:分页参数全局统一。page/page_size或者offset/limit二选一,保持一致。返回结构里带上总数和总页数,前端做分页器的时候就不用再调一次接口去拿总数。


5. 不做版本管理,API裸奔

刚出道那会儿,我觉得版本号是多余的。v1、v2多难看啊,我直接改就行了。结果某天我改了一个字段名,历史版本的应用全部炸裂,用户投诉电话被打爆。

后来我才明白,API是契约。你改了契约,调用方没更新,人家就得给你擦屁股。

正确姿势:URL带版本号。/api/v1/users和/api/v2/users是两个独立的契约,并行跑一段时间,等旧版本自然淘汰再下线。记住,下线比上线更重要。


6. 错误信息等于"操作失败"

这个绝对是我见过最多的垃圾错误信息:

{
  "code": -1,
  "message": "操作失败"
}

操作失败?啥叫操作失败?我登录失败是因为密码错了还是账号不存在?你倒是告诉我啊!这种错误信息除了让调试变成噩梦没别的作用。

正确姿势:错误信息要具体。"账号不存在"、"密码错误,请重试"、"商品库存不足,当前库存0"。如果能给出解决方案就更好了,比如"密码错误,请重试,剩余3次尝试机会"。


7. 敏感数据明文返回

这个是底线,但我真见过有人踩:用户密码明文返回、手机号完整返回、地址信息不加脱敏。问我为啥?"前端说需要展示"。需要个鬼,脱敏展示是前端的事,后端返回完整数据是安全漏洞。

正确姿势:密码绝对不能返回。手机号、身份证、地址等敏感信息后端脱敏返回,比如手机号变成138****5678。除非业务必要,不要返回完整信息。


8. 忽略排序和筛选的默认行为

很多API设计者(包括曾经的我)觉得排序和筛选是可选项,不给也行。结果就是:

GET /api/orders  // 第一次返回按ID排,第二次按创建时间排,鬼知道啥顺序

没有默认排序的接口,返回顺序完全看数据库心情。这种接口前端没法缓存,因为下次可能顺序不一样。

正确姿势:明确默认排序规则。GET /api/orders?sort=created_at&order=desc。调用方不传参数时,返回顺序是确定的、可预期的。


9. 过度抽象,绕晕调用方

有些人喜欢秀设计能力,搞出一堆抽象:

POST /api/resource/action
GET /api/entity/operation
DELETE /api/object/process

resource是什么?action是什么?调用方得拿着字典对照着看。RESTful的美感在于见名知意,别把简单问题复杂化。

正确姿势:名词用复数,动词见名知意。GET /api/users(获取用户列表),POST /api/users(创建用户),DELETE /api/users/123(删除ID为123的用户)。小学生都看得懂就行。


10. 不做接口文档,或者文档和代码不同步

最后这个坑是最致命的。代码改了,文档没改;文档改了,代码没改。总之就是两套信息源,永远对不上。

我现在的方案是强制"文档即代码":用Swagger/OpenAPI注解,代码写完接口文档自动生成。每次部署前检查文档覆盖率,不达标不许上线。

正确姿势:要么用代码生成文档,要么用文档驱动开发。总之不能让文档和代码变成两个独立宇宙,否则最后背锅的还是后端。


写在最后

API设计这事儿,说难也难说简单也简单。难的地方在于它需要经验积累,简单的地方在于原则就那么几条:语义清晰、返回一致、错误具体、文档同步。

别急着写代码,先想清楚接口长啥样。磨刀不误砍柴工,一个好的API设计能让你少加三天班。

如果你觉得这篇文章有用,说明你也踩过类似的坑。评论区说说你的翻车经历,我保证不笑你。

相关文章

凌晨三点,数据库:我超时了,但我不想告诉你为什么
别让API成为同事的噩梦:RESTful设计的血泪经验
当一只小龙虾用上OpenClaw:我的AI助手使用心路
为什么你的API让调用者想砸键盘?
消息队列在生产环境里挖的坑,比你踩过的所有bug加起来还多
为什么你的API总是改来改去?聊聊API版本管理的血泪史

发布评论