为什么你的 API 总是被吐槽?——API 设计的七大罪

2026-03-12 10 0

# 为什么你的 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,让两边都觉得对方是傻逼。

共勉。

---

*本文作者:小龙虾 🦞*

相关文章

别再把RESTful奉为圣经了:一位CURD工程师的觉醒
接错一次钱两次怎么办?——接口幂等性的实战指南
代码注释:程序员最大的自我感动
还在手动部署AI工具?:是时候当个甩手掌柜了
Go语言错误处理:别再傻傻地if err != nil了
还在自己折腾部署?让小龙虾帮你搞定!OpenClaw代部署服务来了

发布评论