我写API被喷了三年,才明白这些坑不能踩

2026-04-20 11 0

我写API被喷了三年,才明白这些坑不能踩

先说个真实故事。三年前,我信誓旦旦给leader说:"这个接口设计得很优雅,符合RESTful规范。"结果上线第一周,前端同学拿着需求来找我对峙:"你这个接口返回的数据结构,我拿到手都不知道往哪儿塞,你是在考验我吗?"

那一刻我明白了:API设计烂不烂,前端同学的身体最诚实。


一、为什么要认真对待API设计?

很多人觉得API就是个"数据搬运工",把数据库的东西搬出来扔给前端就行了。如果你也这么想,恭喜你,三年后的你就是我。

API是产品和后端之间的"合同"。合同写得好不好,决定了:

  • 前端同学会不会在周会上点名骂你
  • 你半夜三点会不会被报警叫醒
  • 你离职之后会不会被前同事念叨(骂的那种)

所以,认真对待API设计,是对同事的善良,也是对自己的救赎。


二、那些年我踩过的API设计大坑

坑1:返回结构随心所欲,一个接口一套规矩

刚出道那会儿,我的返回结构是这样的:

// 用户接口
{"code": 200, "data": {"name": "张三", "age": 25}}

// 订单接口
{"status": "success", "result": {"order_id": "123", "amount": 100}}

// 商品接口
[{"id": 1, "title": "小龙虾"}, {"id": 2, "title": "啤酒"}]

看到问题了吗?code、status、干脆裸数组……一个项目里三个人写了六种风格。前端每次对接都要先来问我:"这个接口的code是0成功还是200成功?"问多了我自己都怀疑人生。

最佳实践:统一响应结构,全项目必须长一个样

{
  "code": 0,
  "message": "success",
  "data": {}
}

code统一用0表示成功,非0表示具体错误码。data永远是你要的业务数据,空也行,别裸奔。message给人类看,code给程序看,分工明确。

坑2:分页设计反人类

让我看看有多少人这样写过分页:

{"items": [...], "count": 100, "page": 1, "pageSize": 20}

看着没问题啊?问题大了:前端拿到这个不知道下一页该传什么。page+1?那offset怎么算?如果业务方说我需要知道"我在第几条",你怎么告诉人家?

推荐:基于游标的分页(Cursor-based Pagination)

{
  "data": [...],
  "pagination": {
    "next_cursor": "eyJpZCI6MTAwfQ==",
    "has_more": true,
    "total": 156
  }
}

前端只需要拿着cursor往下一轮请求里塞,简单粗暴不出错。如果业务需要精确翻页(比如"跳到第5页"),再考虑offset方案,但要和业务方说清楚代价。

坑3:HTTP状态码乱用,404走天下

我见过最离谱的API是这样的:

GET /api/user/123

// 用户不存在
{"code": 404, "message": "用户不存在"}

// 权限不足
{"code": 404, "message": "无权访问"}

// 服务器爆炸
{"code": 404, "message": "内部错误"}

三种错误全返回404,前端看日志以为是同一个问题,排查到怀疑人生。HTTP状态码是给网关、监控、CDN这些基础设施看的,不是给你偷懒用的。

状态码使用规范:

  • 200 OK — 一切正常,有数据
  • 201 Created — 资源创建成功
  • 400 Bad Request — 前端传参有问题,是你代码健壮性不够
  • 401 Unauthorized — 没登录,去登录页
  • 403 Forbidden — 登录了但没权限,别挣扎了
  • 404 Not Found — 资源真的没了
  • 500 Internal Server Error — 服务器挂了,这是你的锅

最烦的就是把所有错误都塞进200加自定义code里,搞得监控系统全是"假成功",报警都不知道什么时候该爬起来。

坑4:接口命名看心情

看看这个项目里的接口名称:

/api/getUserInfo
/api/query_user
/api/user-detail
/api/user_get_one
/api/fetchUser

五个人写了五个版本。RESTful规范不是银弹,但命名一致性是底线。团队里定好规矩:动词用GET/POST/PUT/DELETE,名词全小写复数形式,版本号焊死。

GET    /api/v1/users        # 获取用户列表
GET    /api/v1/users/{id}   # 获取单个用户
POST   /api/v1/users        # 创建用户
PUT    /api/v1/users/{id}   # 更新用户
DELETE /api/v1/users/{id}   # 删除用户

简洁、清晰、不需要文档也能猜到干嘛的接口,才是真正的好接口。


三、版本管理:没你想的那么简单

很多人觉得版本管理就是在URL里加个v1:

/api/v1/users
/api/v2/users

然后v2上线了,v1直接下掉。前端哭了:"我还有三个业务线在用v1啊!"你也哭了:"怎么不早点说!"

版本管理三原则:

  1. 新版本发布后,v1至少再撑6个月。 不是技术做不到,是沟通成本你hold不住。
  2. 字段删除要"过两代"才能删。 今天在v1废弃,下一个主版本再删。
  3. 字段重命名不是覆盖,是新增+废弃。 同时返回两个字段,等客户端全切完了再删旧字段。

记住:你的接口用户不是你,你没有权力单方面宣布"明天v1没了"。


四、字段设计:多一点不如少一点

有一种迷惑行为叫"反正前端可能用得上,我多返回点字段吧":

{
  "id": 123,
  "name": "张三",
  "age": 25,
  "phone": "138xxxx",
  "email": "zhangsan@xxx.com",
  "address": "北京市朝阳区xxx",
  "created_at": "2024-01-01 10:00:00",
  "updated_at": "2024-06-01 15:30:00",
  "last_login_time": "2024-06-18 09:00:00",
  "last_login_ip": "10.0.0.1",
  "user_level": 5,
  "user_points": 3500,
  "vip_expire_time": "2025-12-31 23:59:59",
  "gender": "男",
  "birthday": "1999-06-18",
  "avatar_url": "https://...",
  // ... 等等这还没完
  "ext_data": {}
}

一个列表接口返回50个字段,前端说"我只用三个",你说"那你别用其他的呗"。但问题是:数据传输是有成本的,数据存储是有代价的,数据泄露的风险是真实存在的。

字段设计原则:只返回必要的字段。

如果业务确实需要灵活获取字段,用field筛选:

GET /api/v1/users?fields=id,name,avatar_url

列表接口永远只返回核心字段,详情接口再补全。这不是懒,这是专业。


五、写在最后

API设计这事儿,说难不难,说简单也不简单。技术层面就那些规范,但真正的难点在于你愿不愿意多花10分钟站在调用方角度想一想

每次设计接口之前,先问自己三个问题:

  • 前端拿到这个数据,结构清晰吗?
  • 出错了,定位问题方便吗?
  • 这个接口能优雅地演进吗?

如果三个问题都是yes,那这个接口至少不会让你三年后被人在技术博客里点名吐槽。

当然,如果你看完这篇文章发现自己全踩过,那恭喜你——我们是同行。

欢迎留言说你踩过最离谱的API设计坑,我保证不说是你前同事。🪼

相关文章

当AI开始抢我饭碗时,我的内心OS是这样的
你的API在为谁设计?谈谈幂等性那些事
还在为部署AI工具熬夜?让专业的人来!OpenClaw代部署服务上线
你的后端服务慢得像便秘?问题可能根本不在你写的代码里
你的后端服务慢得像便秘?问题可能根本不在你写的代码里
我见过最烂的API设计,能让开发者当场裂开

发布评论