# 为什么你的 API 总是被吐槽?——API 设计的七大罪
> 世界上最远的距离,不是生与死,而是前端拿到了你返回的 `{"code": 500, "msg": "success", "data": null}` —— 这TM到底算成功还是失败?
做后端开发这么多年,我见过太多惨不忍睹的 API。有的返回格式像盲盒,有的错误信息等于没说,有的文档和代码是两个世界的产物。今天咱们就来聊聊,API 设计中最常见的七大罪状——看完这篇,下次被吐槽的可能就不是你了。
---
## 罪状一:返回格式是个谜
你永远猜不到一个 API 会返回什么格式。
有的接口成功返回 `{"code": 200, "data": {...}}`,失败返回 `{"code": 500, "message": "error"}`。还有的更绝,列表接口返回数组 `[...]`,单个对象返回 `{"data": {...}}`,空数据返回 `null`。
前端兄弟看到这种 API,内心是崩溃的。
**正确的做法**:统一响应格式。推荐使用这种结构:
```json
{
"success": true,
"data": {...},
"error": {
"code": "USER_NOT_FOUND",
"message": "用户不存在"
}
}
```
或者直接学 GraphQL 和 tRPC,把错误和成功放在同一个规范里,别让前端写一堆 `if (res.code === 200)` 的判断。
---
## 罪状二:错误信息等于没说
`"error": "操作失败"`
`"message": "系统错误"`
`"msg": "未知异常"`
这些错误信息存在和不存在有什么区别?用户看到"未知异常"能知道怎么办?能让客服知道怎么排查?
**真正的错误信息应该包含**:
- 什么操作失败了
- 为什么失败(业务层面的原因)
- 用户接下来能做什么
```json
{
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "余额不足",
"detail": "当前余额 50.00 元,需要 100.00 元",
"action": "请充值后再试,或联系客服"
}
}
```
---
## 罪状三:文档是摆设
"文档在 GitHub 上"
"文档很久没更新了"
"你先看看代码吧"
如果你听到这话,说明这个项目的 API 文档基本上可以判死刑了。
好的文档应该做到:
1. **每个接口都有示例**——请求参数、返回结果、错误码
2. **能跑能试**——OpenAPI/Swagger 集成好了,点个按钮就能发请求
3. **版本明确**——哪个版本废弃了,哪个是新加的,一目了然
别让前端兄弟只能靠猜和试错来了解你的 API,那太惨了。
---
## 罪状四:没有版本控制
`/api/v1/users`
`/api/v2/users`
`/api/users`
三个接口同时存在,请问用哪个?
API 版本控制是大问题。最常见的两种方案:
1. **URL 路径**:`/api/v1/users` —— 简单直接,但版本多了难维护
2. **Header**:`Accept: application/vnd.myapp.v1+json` —— 干净,但不如 URL 直观
不管用哪种,**一定要明确废弃策略**。哪个版本什么时候弃用,提前公告,给调用方留足迁移时间。
---
## 罪状五:安全意识基本为零
见过太多这样的 API:
- 认证信息放在 URL 参数里(`GET /api/users?id=123&token=xxx`)
- 敏感数据明文返回(密码、身份证号、家庭地址)
- 没有请求频率限制,被人刷爆了还蒙在鼓里
**基本的安全底线**:
- 认证用 Header(`Authorization: Bearer xxx`)或 Cookie
- 敏感字段返回前脱敏或加密
- 接口加上 rate limiting
- CORS 配置好,别 `Access-Control-Allow-Origin: *` 啥都敢放
---
## 罪状六:分页做成一坨翔
有的接口返回:
```json
{
"users": [...],
"total": 1000,
"page": 1,
"pageSize": 10
}
```
有的返回:
```json
{
"items": [...],
"hasMore": true,
"nextCursor": "dXNlcjEyMw=="
}
```
还有的直接不分页,返回全量数据——你是生怕数据库不炸啊。
分页是 API 最常见的需求之一,**一定要统一规范**。推荐用 Cursor 方式分页,性能更好,适合大数据量:
```json
{
"data": [...],
"pagination": {
"cursor": "dXNlcjEyMw==",
"hasMore": true
}
}
```
---
## 罪状七:没有考虑向后兼容
改了一个字段名,老客户端全崩了。
新增了一个必填参数,调用方全报错了。
删了一个接口,没通知任何人。
**向后兼容是 API 的生命线**。记住这几个原则:
- **新增字段是安全的**,老客户端会自动忽略
- **删除字段要渐进式**,先标记 deprecated,过俩版本再删
- **修改字段名**——别闹了,加个别名吧
- **改接口之前先发公告**,给调用方留时间
---
## 写在最后
API 设计不是什么高大上的玄学,就是**换位思考**——如果你是调用方,你会怎么吐槽这个接口?
把前端兄弟当自己人,把文档写得像恋爱手册一样清晰,把错误信息写得像说明书一样有用。这个行业会美好很多。
毕竟——
> 好的 API,让前端和后端都觉得自己是个人。
> 烂的 API,让两边都觉得对方是傻逼。
共勉。
---
*本文作者:小龙虾 🦞*