3. HTTP 状态码在 BFF 中的正确使用:2xx/3xx/4xx/5xx 状态码语义、自定义业务状态码设计、状态码映射规范
BFF 作为前端与后端微服务之间的“翻译层”,HTTP 状态码的使用不能简单透传后端原始状态码,也不能全部返回 200 然后在响应体中塞错误码。正确的做法是:BFF 根据前端消费习惯重新映射状态码,同时保留业务语义。本章从标准语义、自定义设计、映射规范三个维度展开。
3.1 HTTP 标准状态码在 BFF 中的语义
BFF 应严格区分“通信状态”与“业务状态”。通信状态由 HTTP 状态码表达,业务状态由响应体中的自定义码表达。下表列出 BFF 中最常用的标准状态码及其语义边界:
| 分类 | 状态码 | 标准含义 | BFF 使用场景 |
|---|---|---|---|
| 2xx 成功 | 200 | 请求成功 | 正常返回业务数据;即使业务逻辑失败(如余额不足),也返回 200 + 业务错误码 |
| 2xx 成功 | 201 | 已创建 | BFF 代理创建类接口(如创建订单、注册用户)成功后返回 |
| 2xx 成功 | 204 | 无内容 | 删除操作成功、或批量操作无需返回 body 时使用 |
| 3xx 重定向 | 301/302 | 永久/临时重定向 | BFF 一般不直接返回 3xx,由网关或 CDN 处理;特殊场景如短链服务可透传 |
| 3xx 重定向 | 304 | 未修改 | BFF 缓存层使用 ETag/Last-Modified 时返回,减少带宽 |
| 4xx 客户端错误 | 400 | 请求参数错误 | 参数校验失败、JSON 格式错误、必填字段缺失 |
| 4xx 客户端错误 | 401 | 未认证 | Token 缺失、过期、无效;BFF 应统一拦截并返回 401,不转发到后端 |
| 4xx 客户端错误 | 403 | 无权限 | 用户已认证但无权访问资源;BFF 从后端响应中提取并映射 |
| 4xx 客户端错误 | 404 | 资源不存在 | 请求路径错误、资源已被删除;BFF 应避免暴露后端路径细节 |
| 4xx 客户端错误 | 409 | 资源冲突 | 并发修改、唯一约束冲突、重复提交 |
| 4xx 客户端错误 | 429 | 请求过多 | BFF 限流触发时返回,配合 Retry-After 头部 |
| 5xx 服务端错误 | 500 | 内部错误 | BFF 自身未捕获异常、后端服务不可用且无降级数据 |
| 5xx 服务端错误 | 502 | 网关错误 | BFF 连接后端超时、DNS 解析失败、连接被拒绝 |
| 5xx 服务端错误 | 503 | 服务不可用 | BFF 熔断打开、后端过载、主动拒绝请求 |
| 5xx 服务端错误 | 504 | 网关超时 | 后端响应超时,BFF 在超时阈值内未收到响应 |
关键原则:BFF 永远不要将后端 500 直接透传给前端。后端 500 应被 BFF 转换为 502/503/504,或降级为 200 + 业务错误码(如“服务繁忙,请稍后重试”)。
3.2 自定义业务状态码设计
HTTP 状态码只有几十个,无法表达千差万别的业务语义。因此 BFF 必须在响应体中携带自定义业务状态码。设计时遵循“三段式”结构:
// 响应体统一格式
{
"code": 100200, // 业务状态码
"message": "success", // 人类可读描述
"data": { ... }, // 业务数据
"traceId": "abc123" // 链路追踪 ID
}
三段式编码规则:
- 第一位(1位):错误来源。1=系统级,2=业务级,3=安全级,4=第三方依赖。
- 第二位(2位):模块编号。01=用户,02=订单,03=支付,04=商品,99=通用。
- 第三位(3位):具体错误编号。从 001 开始递增,预留 100 个空位。
| 业务码 | 含义 | HTTP 状态码 | 说明 |
|---|---|---|---|
| 100200 | 成功 | 200 | 请求处理正常 |
| 100400 | 参数校验失败 | 200 | 业务错误,不返回 400 |
| 100401 | Token 过期 | 401 | 需重新登录 |
| 100403 | 无操作权限 | 403 | 权限不足 |
| 200001 | 用户不存在 | 200 | 业务查询正常,但数据为空 |
| 200201 | 订单状态不允许取消 | 200 | 业务逻辑冲突 |
| 200301 | 支付金额不一致 | 200 | 需前端提示用户确认 |
| 300001 | 请求被限流 | 429 | 触发 BFF 限流 |
| 300002 | 请求被熔断 | 503 | 触发 BFF 熔断 |
| 400001 | 第三方服务超时 | 504 | 后端响应超时 |
| 400002 | 第三方服务异常 | 502 | 后端返回非预期响应 |
设计建议:业务状态码的“成功”码固定为 100200,不要使用 0 或 20000 等容易与 HTTP 状态码混淆的值。前端 SDK 统一判断
code === 100200 为成功,其余均为失败。
3.3 状态码映射规范
BFF 从后端微服务获取响应后,需要执行“三层映射”才能返回给前端。映射流程如下图所示:
映射规范总结为四条硬规则:
- 通信错误透传:后端返回 4xx 客户端错误(401/403/404/429),BFF 直接透传对应 HTTP 状态码,不修改状态码分类。
- 服务端错误降级:后端返回 5xx,BFF 必须转换为 502/503/504,或降级为 200 + 业务码 400xxx,绝不允许透传 500。
- 业务错误统一为 200:后端业务逻辑异常(如“订单已取消”“库存不足”),BFF 统一返回 HTTP 200,业务语义由自定义码表达。
- BFF 自身异常独立编码:BFF 限流、熔断、超时等自身故障,使用 429/503/504 + 3xxxx 业务码,与后端错误隔离。
3.4 映射实现示例(Node.js)
// BFF 状态码映射中间件(Node.js + Express 示例)
function statusCodeMapper(req, res, next) {
// 保存原始 res.json 方法
const originalJson = res.json.bind(res);
res.json = function (body) {
// 如果响应体已有自定义格式,不重复包装
if (body && typeof body.code === 'number') {
return originalJson(body);
}
// 根据 HTTP 状态码决定映射策略
const status = res.statusCode;
if (status >= 200 && status < 300) {
// 成功:包装为 100200
return originalJson({
code: 100200,
message: 'success',
data: body,
traceId: req.traceId
});
}
if (status >= 400 && status < 500) {
// 客户端错误:透传状态码,业务码映射
const codeMap = {
400: 100400,
401: 100401,
403: 100403,
404: 100404,
429: 300001
};
return originalJson({
code: codeMap[status] || 100400,
message: body?.message || '客户端请求错误',
data: null,
traceId: req.traceId
});
}
if (status >= 500) {
// 服务端错误:降级为 502/503/504,业务码 4xxxx
const downgradeStatus = status === 504 ? 504 : 502;
res.status(downgradeStatus);
return originalJson({
code: 400001,
message: '服务暂时不可用,请稍后重试',
data: null,
traceId: req.traceId
});
}
return originalJson(body);
};
next();
}
避坑提醒:不要在前端 SDK 中同时判断 HTTP 状态码和业务码。统一约定:前端只判断
code === 100200 为成功,HTTP 状态码仅用于浏览器网络面板调试和 CDN/网关缓存策略。如果前端同时判断 HTTP 200 和 code 100200,会导致后端降级场景(HTTP 502 + code 400001)被错误处理。
3.5 状态码映射规范检查清单
| 检查项 | 通过标准 | 常见违规 |
|---|---|---|
| 后端 500 透传 | BFF 转换为 502/503/504 或降级为 200 | 直接返回 500 给前端 |
| 业务错误状态码 | 统一返回 HTTP 200 + 业务码 | 返回 400/409 等 HTTP 错误码 |
| 限流/熔断状态码 | 429/503 + 3xxxx 业务码 | 返回 500 或 200 + 通用错误 |
| 成功响应格式 | code=100200, data 不为 null | 直接透传后端原始 JSON |
| 错误响应格式 | code != 100200, data=null, message 有值 | data 字段返回错误堆栈 |
| traceId 传递 | 所有响应均携带 traceId | 错误响应缺少 traceId |
通过以上规范,BFF 能够为前端提供可预测、可枚举、可自动化处理的错误响应体系。前端团队只需维护一张业务码映射表,即可覆盖所有异常场景,无需关心后端微服务的具体实现细节。