前端API错误码设计为何总在两端扯皮?我的一份建议规范

阿乐
阿乐 管理员 黑卡会员
发布于 2026-09-12 13:52 ·3 浏览 ·0 回复

几乎每个前端团队都经历过这样的场景:联调群里甩出一张截图,接口返回 `200`,`code` 是 `5001`,页面白屏。前端问后端“这个码什么意思”,后端回“文档里有”;前端翻文档,发现文档上写的 `5001` 是“余额不足”,而这次报的是“活动已结束”。于是一来一回,问题从技术问题变成了态度问题。

扯皮的根源从来不是谁不专业,而是边界没划清。HTTP 状态码、业务错误码、展示文案,这三件事被硬塞进了一个字段里,谁都能解释,谁都不负责。

根源:三重语义被压进同一个字段

一个错误响应其实同时承载了三种完全不同的信息:

- 传输层:这次请求在网络上、协议上是否成功(HTTP status)
- 业务层:这次操作在业务规则上为什么失败(业务码)
- 展示层:这句话要不要给用户看、怎么看(文案与交互)

当后端用 `HTTP 200 + code:500` 表达业务失败时,监控、网关、重试中间件全部失明;当前端拿到 `message: "系统异常"` 直接弹 toast 时,用户看到的是开发者的内部语言。三者混用,扯皮就是必然。

五个典型翻车现场

1. 一切皆 `200`,业务失败藏在自己的 `code` 里,网关和 APM 无法统计错误率。
2. `message` 直接透传给用户,出现 `NullPointerException` 或者英文堆栈。
3. 错误码复用且无命名空间,`1001` 在订单模块是“库存不足”,在用户模块是“未登录”。
4. 前端硬编码判断 `if (res.code === 40001)`,后端一改码,前端全量发版。
5. 文档漂移:接口加了新码,文档还是三个月前的版本。

建议规范:三层分离,各归其位

| 层 | 定义方 | 消费方 | 示例 |
| --- | --- | --- | --- |
| HTTP 状态码 | 网关/框架 | 中间件、监控 | 200 / 400 / 401 / 429 / 500 |
| 业务错误码 | 业务后端 | 前端、SDK | `10201` 库存不足 |
| 展示文案 | 后端给默认值,前端可覆写 | 用户 | “该商品暂时缺货” |

原则一句话:**HTTP 状态码只表达传输结果,业务码只表达业务语义,文案只负责展示,三者不互相替代。**

编码规则:分段、定长、可读

建议 6 位字符串:`模块(2) + 类型(2) + 序号(2)`,例如 `10-20-01`。

类型段可以固定含义:

- `10` 参数类(客户端可自查)
- `20` 认证授权类(需要跳登录或无权限提示)
- `30` 业务规则类(需要展示具体原因)
- `40` 依赖类(下游服务、第三方)
- `50` 系统类(未知,兜底)

全局预留 `0` 表示成功,`1xxxxx` 作为通用码(未登录、token 过期、无权限、限流)。码用字符串而非数字,避免 `0` 与 `"0"`、前导零、JS 精度这些历史坑。

统一响应体

{
  "code": "30201",
  "message": "sku 1234 stock insufficient",   // 给开发看
  "userMessage": "该商品暂时缺货",              // 给用户看,可空
  "traceId": "a1b2c3d4",
  "data": null
}

`message` 面向排查,允许技术细节;`userMessage` 面向终端,前端没有覆写需求时直接用;`traceId` 是排障时双方唯一不用吵架的凭据。

前端只认「可操作性分类」,不认具体码

前端不应该、也没必要理解每一个业务码。它只需要知道这个错该做什么动作:

| 前缀 | 前端动作 |
| --- | --- |
| `10xxxx` | 表单校验或参数修正提示 |
| `12xxxx` | 跳转登录 / 刷新 token |
| `13xxxx` | 展示后端 `userMessage` |
| `14xxxx` | 静默重试或降级 |
| `15xxxx` / 未知 | 统一兜底 toast + 埋点上报 |

具体码只在需要特殊交互(比如弹确认框)时例外处理,且必须写进 registry 备注。

单一事实来源与类型生成

维护一份 `error-codes.yaml`,由它生成三样东西:接口文档、后端枚举、前端 TS 联合类型。

type BizCode = '10001' | '10002' | '30201' | '30202';

这样前端写 `switch` 时是编译期检查,后端改码时是 CI 报错,而不是线上事故。

变更规则:只增不删不改语义

- 新增码:走新序号,不占用废弃码。
- 废弃码:标 `deprecated`,保留至少两个大版本。
- 修改语义:禁止。语义变了就是新码。
- 删码:需要跨端评审,不允许单方面操作。

一份最小落地清单

1. 业务码与 HTTP 状态码解耦,HTTP 只表达传输结果。
2. 错误码注册中心化,文档和类型由它生成。
3. 响应体固定包含 `code / message / userMessage / traceId`。
4. 前端按前缀做分类兜底,不硬编码具体码。
5. 变更走评审,废弃有过渡期。

扯皮的本质是权责不清,不是能力问题。把“谁定义、谁消费、怎么改”写进规范,两端就不会再为同一个 `5001` 来回甩截图了。规范不用一次到位,但第一次对齐之后,后面每一次联调都会便宜一点。

本文转载自 阿乐技术社区,原文地址:https://www.leleweb.cn/thread-265.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~