所有 Servlet 栈接口的成功与失败响应都由框架包成同一个信封:
{
"success": true,
"result": { "id": 123 }
}{
"success": false,
"code": "61001",
"message": "参数不合法"
}字段说明:
| 字段 | 说明 |
|---|---|
success |
业务成功 / 失败 |
code |
错误码(字符串形式的数字),成功时不返回 |
message |
提示信息(已本地化),成功时不返回 |
result |
业务数据;失败时可承载调试详情,非开发环境一律为 null |
信封为 null 字段不参与序列化(@JsonInclude(NON_NULL)),所以成功响应里看不到
code / message。
HTTP 状态码表达协议语义,code 表达业务语义,两者并存:
| 场景 | HTTP | success |
code |
|---|---|---|---|
| 成功 | 200 | true |
无 |
| 参数校验失败 | 400 | false |
业务错误码 |
| 未认证 | 401 | false |
业务错误码 |
| 无权限 | 403 | false |
业务错误码 |
业务规则拒绝(BusinessException) |
200 | false |
业务错误码 |
| 未预期异常 | 500 | false |
业务错误码 |
| Servlet Sentinel 拦截 | 429 | false |
61006 |
业务规则拒绝走 HTTP 200,由 success=false + code 表达。
Servlet 服务接入 Sentinel 后,BlockException 由 MVC starter 返回 HTTP 429 与
61006;网关限流仍返回 HTTP 429 与网关区段的 63006。两侧使用相同的失败信封和
用户提示,错误码归属各自组件。Servlet 服务应在 i18n/error-code.properties、
i18n/error-code_zh_CN.properties 与 i18n/error-code_en_US.properties 中提供
error.code.61006,中文为“请求过于频繁,请稍后再试”,英文为
“Too many requests, please try again later”。若规范 key、兼容的纯数字 key 与
mars.codes 都没有文案,则使用固定英文提示。
个别接口(如文件下载、回调)需要原样返回时,在类或方法上标注
@IgnoreResponseAnnotation 即可跳过包装。
归属由错误码的前两位唯一决定,分配表写在框架里(FrameworkErrorCodeRange),
应用侧不需要也不应该把这些数字抄到配置里。
| 前缀 | 区段 | 归属 |
|---|---|---|
60 |
60000–60999 |
框架级(mars-cloud-common) |
61 |
61000–61999 |
mars-cloud-mvc-spring-boot-starter |
62 |
62000–62999 |
mars-cloud-security-spring-boot-starter |
63 |
63000–63999 |
网关 |
64 |
64000–64999 |
认证服务(auth-service) |
65 |
65000–65999 |
权限服务(UPMS) |
66+ |
66000–99999 |
业务服务(含示例服务) |
框架各层也必须声明自己的区间——框架自己抛出的错误同样需要错误码。
mars:
error-code:
validate: true # 默认 true,启动时校验
# 框架层:列出要用到的层,区间取自框架分配表
framework-layers: [ common, mvc ]
# 业务服务:声明自己那一段(多个服务各自声明,互不重叠)
ranges:
- owner: business
start: 66000
end: 66999owner 必须是分配表里的归属名。业务服务用 business;多个业务服务共用这一个区段,
各自声明一段即可(例如 A 服务 66000–66999、B 服务 67000–67999)。
各服务实际占用的子区间登记在服务仓的 docs/services.md,避免两个服务挑中同一段。
可运行的完整接线示例见服务仓的 mars-cloud-sample-service。
四类校验,全部在启动期完成,失败即拒绝启动:
| 校验 | 触发场景 | 报错示例 |
|---|---|---|
| 配置自检 | 归属名不存在、区间起止写反、声明超出分配区段、段间重叠 | [business] 声明的区间 [66000, 100000] 超出该归属的分配区段 [66000, 99999] |
| 归属校验 | 错误码所属的层没有启用 | 错误码 62001 属于 [security] 区段,但该区段未启用 |
| 越界校验 | 错误码不在本服务声明的段内 | 错误码 66099 不在 [business] 已声明的任何区间内 |
| 重复校验 | 同一个错误码注册两次 | 错误码重复注册: 66002 |
段间重叠这条不是冗余检查:多个业务服务共用 business 区段时,只有它能发现
「A 服务占了 66000–66499,B 服务又占了 66400–66899」这类撞车。
这样错误码冲突会在启动瞬间暴露,而不是等到某个分支被触发。
校验对象是「注册进来的业务码」:只有通过 ErrorCodeRegistrar 注册的错误码才做归属、
越界与重复校验。框架自身兜底用的码(如参数校验失败的 400、未预期异常的 500)
不经这套校验,也不要求落在任何声明区段内——所以你在响应里看到 code: "400"
是正常的,不必去 ranges 里为它开一段。
实现 ErrorCode 接口即可(枚举是最常见的形式):
@Getter
@AllArgsConstructor
public enum OrderErrorCode implements ErrorCode {
ORDER_NOT_FOUND(66001),
ORDER_ALREADY_PAID(66002),
;
private final int code;
}把它们注册给框架:
@Component
public class OrderErrorCodeRegistrar implements ErrorCodeRegistrar {
@Override
public Collection<? extends ErrorCode> codes() {
return List.of(OrderErrorCode.values());
}
}抛出业务异常:
throw new BusinessException(OrderErrorCode.ORDER_NOT_FOUND);错误码只存数字,文案按 key 约定从 MessageSource 取:
| 项 | 约定 |
|---|---|
| key 格式 | error.code.<数字> |
| 资源文件 | i18n/error-code.properties(默认)、_zh_CN、_en_US 三份同步维护 |
| 必需配置 | spring.messages.basename: i18n/error-code,缺它会退化成裸错误码 |
error.code.66001=订单不存在文案查找有四级兜底,逐级下落直到命中:
| 级 | 来源 | key / 位置 |
|---|---|---|
| ① | i18n 资源,规范 key | error.code.<数字> |
| ② | i18n 资源,纯数字 key | <数字> |
| ③ | 服务本地的兜底配置 | config/exception-code.properties 里的 mars.codes[<数字>] |
| ④ | 最终回落 | 数字码本身 |
第 ② 级是为兼容历史资源写法保留的,容易被漏掉——只按①③④实现会丢掉一档兜底。
另注意:HttpException#getErrorMsg() 只走 ① 与 ②,不查 ③;
只有响应体(advice 层)会走完四级。也就是说「要不要用兜底配置,取决于读的是异常自带文案还是响应体」。