别把API设计成玄学:我踩过的10个设计大坑

2026-04-22 8 0

别把API设计成玄学:我踩过的10个设计大坑

做后端开发这些年,我写过不少API,也接别人的API更多。好的API设计大家都说好,但说不出好在哪;烂的API设计呢,你一眼就知道不对劲,但就是说不清楚。今天把我踩过的那些坑整理出来,给大家提个醒,少走弯路。

坑一:URL命名看心情,接口地址像乱码

见过最离谱的接口长这样:

/api/v1/getUserInfo?id=123
/api/v1/query_user_data_by_id
/api/v1/uinfo

同一个项目,三种风格。这还算好的,更有甚者:

/api/getChinaUserListNewFinal
/api/getChinasrInfo

你告诉我"chinasr"是什么?中国什么?

正确做法:统一命名规范,用名词复数,用连字符分隔:

GET /api/users          # 用户列表
GET /api/users/{id}     # 单个用户
POST /api/users         # 创建用户

RESTful不是银弹,但至少让接口地址可预测。前端只要知道资源名,大概能猜出接口地址,这才是好的设计。

坑二:GET做写操作,POST做读操作

这种我也见过不少:

GET /api/deleteUser?id=123
GET /api/updateUser?id=456&name=test

问:这种接口有什么问题?

答:问题大了。GET请求会被浏览器缓存,会被日志记录,会被CDN缓存,可能被搜索引擎抓取。用GET做写操作,等于把数据库裸奔在网络上。

更骚的操作是:

POST /api/getUserInfo  # 用POST做查询
Content-Type: application/x-www-form-urlencoded
id=123

这完全是反人类设计。HTTP语义清清楚楚,不要为了"安全"或者"避免缓存"去违反它。

坑三:HTTP状态码乱返回,200表示"服务器爆炸了"

见过最离谱的错误处理:

{
  "code": 200,
  "message": "服务器爆炸了,请稍后重试",
  "data": null
}

code=200,但message说"服务器爆炸了"。前端判断code=200就认为成功,结果用户看到的是"成功"但数据是空的,体验稀碎。

正确做法:让HTTP状态码反映结果,body里的code作为业务状态码:

HTTP 200 OK
{
  "code": 0,
  "message": "success",
  "data": {...}
}

HTTP 400 Bad Request
{
  "code": 40001,
  "message": "参数校验失败",
  "errors": [...]
}

HTTP 500 Internal Server Error
{
  "code": 50001,
  "message": "服务不可用,请联系管理员",
  "data": null
}

这样做的好处:HTTP状态码给基础设施(网关、负载均衡)看,业务状态码给前端看,各司其职。

坑四:返回字段不过滤,一个接口暴露整个数据库

用户列表接口,返回字段包括:password_hash、id_card、phone、email……这不是API,这是数据泄露通道。

我见过一个接口,返回了用户的:

{
  "id": 123,
  "username": "zhangsan",
  "email": "zhangsan@example.com",
  "phone": "13800138000",
  "password": "e10adc3949ba59abbe56e057f20f883e",  // !!!
  "created_at": "...",
  "updated_at": "...",
  "last_login_ip": "...",
  "last_login_time": "...",
  "is_admin": true,  // !!!
  "password_changed_at": "...",
  "security_question": "..."  // !!!
}

这套数据要是流出去,就是一场数据安全事故。

正确做法:指定返回字段,敏感字段默认不返回:

GET /api/users?fields=id,username,email,created_at
GET /api/users/{id}?fields=id,username,email

或者用GraphQL,按需取字段,别一股脑全倒出来。

坑五:分页参数全靠猜,每家接口不一样

见过太多种分页方式:

page=1&size=20          # 方式1
offset=0&limit=20       # 方式2
start=0&count=20        # 方式3
pageNo=1&pageSize=20    # 方式4
cursor=abc123           # 方式5

更离谱的是,同一个项目里这几种混用。

统一分页规范,推荐用cursor-based分页,适合大数据量和高并发场景。如果数据量可控,page-based也可以,但要在文档里明确写清楚参数名和默认值。

GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20
Response:
{
  "data": [...],
  "pagination": {
    "next_cursor": "eyJpZCI6MTQzfQ",
    "has_more": true,
    "total": 1000
  }
}

坑六:错误信息等于没说,前端看了想打人

错误信息最烂的写法:

{
  "error": "操作失败"
}

操作失败?什么操作?为啥失败?是服务器炸了还是参数错了?前端看到这个错误信息,除了弹一个"操作失败"给用户,啥也干不了。

好的错误信息应该包含:

  1. 发生了什么问题(用户能看懂的描述)
  2. 为什么发生(可选,帮助用户自查)
  3. 怎么解决(可选,给用户指条路)
  4. 错误码(给技术支持/前端判断用)
{
  "code": 40001,
  "message": "手机号格式不正确",
  "detail": "当前手机号 13800138000 长度或格式不符合中国大陆手机号规范",
  "solution": "请输入11位数字,以1开头",
  "request_id": "req_abc123"  // 用于排查问题
}

坑七:不做版本管理,一升级全崩溃

很多项目一开始:

/api/users

然后有一天需要Breaking Change,加字段、改逻辑、删字段,怎么办?没有版本管理的话,只能:

  • 新增字段,旧接口返回新字段 → 旧版前端解析报错
  • 删字段 → 旧版前端用到了这个字段,直接崩溃
  • 改字段类型 → 旧版前端类型判断直接失效

正确做法:从第一天就加版本:

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

新版本上线后,给旧版本留足过渡时间(比如6个月),逐步废弃。API版本不是矫情,是对自己和调用方负责。

坑八:认证鉴权一把梭,token满天飞

见过最随意的鉴权设计:

GET /api/users?token=abc123
GET /api/users?api_key=xyz789
Header: X-Token: abc123

三种方式在一个项目里同时存在,安全性参差不齐,token泄露了也不知道。

统一鉴权方案,推荐JWT:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

同时注意:

  • token要设置过期时间,别搞永久token
  • refresh_token和access_token分开
  • 敏感操作二次验证(支付、修改密码等)
  • 服务端做好token主动失效机制

坑九:忽略幂等性,重试一次灾难一次

接口没有幂等性保证,用户网络不好点了一下重试,结果:

POST /api/orders
# 第一次:创建订单,扣款200元
# 重试:又创建订单,又扣款200元
# 用户:???我买了一个还是两个???

正确做法:

1. 客户端生成唯一请求ID(UUID)
2. 服务端做幂等键(idempotency key)检查
3. 重复请求直接返回上次的结果

Header: Idempotency-Key: uuid-v4-string

Response:
{
  "code": 0,
  "data": {
    "order_id": "order_123",
    "status": "created"
  },
  "idempotent_replay": false  // 标记是否是重复请求
}

POST类写操作接口,一定要有幂等性保证。这不是优化,是工程底线。

坑十:文档残缺不全,注释比代码还少

见过最精简的API文档:

/api/users - 获取用户列表

没了。没有参数,没有返回值,没有错误码,没有示例。这不是文档,这是标题。

好的API文档应该包含:

  • 接口描述(这个接口干什么用)
  • 请求参数(名称、类型、是否必填、默认值、说明)
  • 响应格式(成功/失败的例子都要给)
  • 错误码对照表
  • 调用示例(curl、JavaScript、Python各来一个)
  • 频率限制说明

如果文档写不清楚,说明你自己也没想清楚这个接口该怎么用。先想清楚,再动手写代码。

总结:好API是设计出来的,不是改出来的

API设计不是后端开发的附属品,是产品体验的一部分。前端调用方看你接口的体验,就像用户看UI的体验一样直观。

几个核心原则:

  1. 语义清晰:HTTP语义、URL命名、状态码,都要符合规范
  2. 安全第一:敏感字段不返回,认证鉴权要到位
  3. 可预测:接口命名一致,参数格式统一,不要让人猜
  4. 容错友好:错误信息有用,分页有规则,幂等有保证
  5. 文档先行:写代码之前先想接口设计,接口设计之前先写文档

API是你给调用方的承诺,一旦发布就是契约。改一次代价很大,所以在发布之前多想一想,把坑填平。

祝大家的API都能稳定运行,少踩坑,多划水。

相关文章

「搞不定部署?」来,小龙虾帮你一键搞定 🦞
「搞不定部署?」来,小龙虾帮你一键搞定 🦞
我删了一行SQL,公司差点给我发律师函
别再用RESTful了,你的API设计可能从根子上就错了
🤖 AI探索|最近我在信息洪流里捞到的好东西
还在为部署AI工具抓狂?让小龙虾帮你搞定!🦞

发布评论