泛型类 result 封装 rpc 响应体,仅保留 code(业务码)、message(纯文本提示)、data(泛型业务数据)三字段;通过泛型静态工厂方法、controller 强制声明、openapi schema 与客户端代码生成,确保前后端类型一致、不破型、可直接使用。

用泛型类封装 RPC 响应体,核心是让 code、message、data 三字段稳定可预期,同时确保 T data 在任意业务场景下不破型、不丢失语义——不是“能塞进去”,而是“塞进去后前端敢直接用、后端敢直接序列化、IDE 敢直接跳转”。
只保留三个必选字段,拒绝语义污染
响应体结构必须精简且无歧义:
- int code:自定义业务码(如 10000=成功,40001=参数缺失),不用 HTTP 状态码;网关或监控系统需靠它做路由与告警,不能和 HTTP 层混用
- String message:纯文本提示,禁止拼接堆栈、路径、JSON 片段;前端 toast、日志归集、错误上报都依赖它,一串乱码或空字符串会直接阻断问题定位
-
T data:唯一承载业务数据的泛型字段,类型由接口声明决定(如
Result<user></user>或Result<list>></list>),不允许出现Object、Map<string object></string>这类擦除型返回
删掉 timestamp、success、extend 等字段。它们要么该由网关统一注入,要么属于特定子域扩展(如审计场景可继承 ResultAudit<t></t>),不应污染通用契约。
泛型静态工厂方法必须支持类型推导
所有构造入口必须是泛型静态方法,否则编译器无法绑定 T,前端收到 data: {} 或后端反序列化为 LinkedHashMap 就是典型破型表现:
-
public static <t> Result<t> success(T data)</t></t>—— 传入new User("Alice"),返回即为Result<user></user> -
public static <t> Result<t> fail(int code, String message)</t></t>—— 即使data为 null,也维持Result<product></product>类型,避免 JSON 反序列化失败 -
public static <t> Result<t> empty()</t></t>—— 显式表达“有结果、但 data 为空”,比null更安全,前端可统一判空而无需 try-catch
禁用 Result.of(Object) 这类非泛型方法,它会让 Result<order> result = Result.of(order)</order> 编译报错或隐式擦除为 Result<object></object>。
Controller 层强制声明泛型返回类型
所有 REST 接口必须写成 Result<x></x>,这是堵住字段黑洞的第一道闸门:
- 前端 Axios 拦截器可统一提取
response.data.data,无需每个接口写不同路径 - Swagger 自动生成的 Schema 是确定的:
data字段类型永远等于你写的<user></user>或<page>></page>,而不是object - 如果已有老接口未改造,可用
@RestControllerAdvice拦截非Result返回值,自动包装为Result.success(value),但要排除已返回Result或抛异常的情况,避免二次封装
配合 JSON Schema 与强类型客户端生成
光有 Java 泛型不够,字段黑洞常发生在前后端类型不同步时。需配套落地:
- 在 OpenAPI YAML 中明确定义
Result的 Schema,将data描述为$ref: '#/components/schemas/User',而非type: object - 用
openapi-generator生成 TypeScript 客户端,得到ResultUser类型,字段名、是否可选、嵌套结构全部与后端一致 - 对
data字段启用严格校验:如后端声明Result<bigdecimal></bigdecimal>,前端生成类型就是string(因 JSON 无 BigDecimal),并配正则校验格式,杜绝 “123.00” 被当成整数解析











