后端错误码设计原则:HTTP状态码与业务错误码的区分、错误码分段规则、错误码语义化设计

在集成测试环境下,错误码是后端服务与调用方(包括测试断言)之间最核心的契约。设计混乱的错误码会导致测试脚本难以维护、故障定位效率低下,甚至引发安全合规风险。本章从三个维度阐述后端错误码的设计原则:HTTP状态码与业务错误码的区分、错误码分段规则、错误码语义化设计。

1. HTTP状态码与业务错误码的区分

HTTP状态码(如 200、400、500)用于表达传输层与协议层的结果,而业务错误码用于表达应用层与业务逻辑层的具体错误原因。两者必须严格分离,不可混用。

维度 HTTP 状态码 业务错误码
职责 表示请求的传输结果(成功、客户端错误、服务端错误) 表示业务逻辑的具体失败原因(如余额不足、权限不足)
粒度 粗粒度(约 60 个标准码) 细粒度(可扩展至数千个)
变更频率 极低(RFC 标准,几乎不变) 随业务迭代频繁变更
安全合规关注点 避免泄露内部信息(如 500 不应带堆栈) 避免暴露敏感业务逻辑或数据
集成测试断言 断言响应状态码是否在预期范围内 断言业务错误码是否匹配预期值
核心原则:HTTP 状态码只回答“请求是否被正确接收和处理”,业务错误码回答“如果处理失败,具体原因是什么”。集成测试中,两者必须同时断言,缺一不可。

典型响应体结构示例:

{
  "code": 100301,
  "message": "用户账户余额不足",
  "detail": "当前余额: 10.50,所需金额: 200.00",
  "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

此时 HTTP 状态码为 400 Bad Request,业务错误码 100301 精确标识了“余额不足”这一业务场景。

2. 错误码分段规则

错误码应采用数字分段方式,将不同模块、不同严重级别、不同错误类型映射到固定的数字区间。分段规则是错误码体系可维护、可扩展的基础。

错误码分段规则结构图 错误码:6 位数字,分段结构:XX YY ZZ XX:模块标识 01-19 用户模块 YY:错误类型 00-49 参数校验 ZZ:具体错误 00-99 错误序号 示例:错误码 100301 10 → 用户模块(XX) 03 → 账户相关错误(YY) 01 → 余额不足(ZZ) 常见分段区间: 01-19:用户/认证模块 | 20-39:订单/交易模块 | 40-59:支付/财务模块 60-79:商品/库存模块 | 80-99:系统/基础设施模块
推荐分段策略:采用 6 位数字编码(XXYYZZ),前两位标识模块,中间两位标识错误大类,后两位标识具体错误。预留足够的扩展空间,避免后期重构。

3. 错误码语义化设计

语义化设计要求错误码本身能够传达足够的上下文信息,使开发人员、测试脚本、运维人员无需查阅文档即可大致理解错误性质。语义化包含三个层面:可读性、可分类性、可搜索性。

3.1 可读性:错误码与错误消息的配对

每个错误码必须绑定一个固定的、面向开发者的错误消息(message),以及一个可选的面向用户的用户提示(userHint)。消息内容应遵循以下规范:

  • 动词优先:以“缺少”、“无效”、“超过”、“禁止”等动词开头。
  • 参数化:使用占位符代替具体值,避免硬编码。
  • 无歧义:同一错误码只能对应一种错误场景。
// 反例:语义模糊
100401: "操作失败"

// 正例:语义清晰
100401: "用户 [userId] 对资源 [resourceId] 无 [permission] 权限"

3.2 可分类性:按严重级别与错误来源分段

在分段规则基础上,进一步按严重级别划分区间,便于集成测试设置不同的断言策略:

错误码区间 严重级别 集成测试断言策略
XX00-XX49 参数校验错误 断言 HTTP 400,业务码精确匹配
XX50-XX79 业务逻辑错误 断言 HTTP 400/409,业务码精确匹配
XX80-XX89 权限/安全错误 断言 HTTP 403,业务码精确匹配,检查安全审计日志
XX90-XX99 系统内部错误 断言 HTTP 500,业务码精确匹配,检查是否泄露堆栈

3.3 可搜索性:错误码的命名与文档化

错误码应作为一等公民纳入 API 文档和测试用例管理。推荐做法:

  • 在代码中以枚举或常量形式定义,禁止魔法数字。
  • 每个错误码附带 @since、@deprecated 等元数据。
  • 集成测试中直接引用错误码常量,而非硬编码数字。
// Java 枚举示例
public enum ErrorCode {
    USER_BALANCE_INSUFFICIENT(100301, "用户账户余额不足"),
    ORDER_NOT_FOUND(200101, "订单不存在"),
    PAYMENT_TIMEOUT(400201, "支付超时");

    private final int code;
    private final String message;

    ErrorCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() { return code; }
    public String getMessage() { return message; }
}
安全合规红线:错误码 message 中禁止包含敏感信息(如数据库表名、SQL 片段、完整堆栈、用户密码、Token 明文)。集成测试应额外断言:当错误码为系统内部错误(XX90-XX99)时,响应体不包含 stackTrace 或 exception 字段。

4. 集成测试中的错误码断言实践

基于上述设计原则,集成测试断言应遵循以下模板:

// 伪代码示例
Response response = given()
    .header("Authorization", "Bearer " + token)
    .body(requestBody)
    .when()
    .post("/api/v1/order/create");

// 第一步:断言 HTTP 状态码
response.then().statusCode(400);

// 第二步:断言业务错误码
int bizCode = response.jsonPath().getInt("code");
assertThat(bizCode).isEqualTo(ErrorCode.USER_BALANCE_INSUFFICIENT.getCode());

// 第三步:断言错误消息不含敏感信息
String message = response.jsonPath().getString("message");
assertThat(message).doesNotContain("stackTrace", "SQL", "password");

通过严格遵循 HTTP 状态码与业务错误码的分离、数字分段规则、以及语义化设计,集成测试脚本可以获得稳定、可预测、可维护的错误断言基础,同时满足安全合规的审计要求。