在实际项目开发中,仅仅返回原始数据(如对象、列表)是远远不够的。前端、移动端或其他服务调用方需要明确知道本次请求是否成功、失败原因是什么、以及附带哪些业务数据。
因此,设计一个统一、规范、可扩展的 JSON 响应结构,是构建高质量 RESTful API 的关键一步。
本节课将带你:
- 设计通用的
JsonResult<T>响应类; - 在 Controller 中使用泛型封装返回值;
- 理解统一响应格式在前后端协作中的价值。
1. 为什么需要统一返回结构?
试想以下场景:
- 用户登录失败,后端只返回
{}或null,前端无法判断是“密码错误”还是“网络超时”; - 分页接口返回一个 List,但没有总条数、当前页码等元信息;
- 不同接口返回格式不一致:有的有
code,有的没有;有的错误信息在msg,有的在error。
这些问题会导致:
- 前端需要写大量判空和兼容逻辑;
- 联调效率低下;
- 接口难以维护和文档化。
✅ 解决方案:所有接口返回统一结构体,包含:
code:状态码(如 "0" 表示成功,"500" 表示系统错误);msg:提示信息(如 "操作成功!"、"用户名不能为空");data:实际业务数据(可以是对象、列表、Map 等)。
2. 定义统一的 JSON 响应类:JsonResult<T>
由于 data 字段的类型不确定(可能是 User、List、PageInfo 等),我们使用 Java 泛型 来实现通用性。
public class JsonResult<T> { private T data; private String code; private String msg; // 默认构造:成功,无数据 public JsonResult() { this.code = "0"; this.msg = "操作成功!"; } // 自定义 code 和 msg(常用于错误) public JsonResult(String code, String msg) { this.code = code; this.msg = msg; } // 有数据,成功 public JsonResult(T data) { this.data = data; this.code = "0"; this.msg = "操作成功!"; } // 有数据 + 自定义提示 public JsonResult(T data, String msg) { this.data = data; this.code = "0"; this.msg = msg; } // getter / setter(建议使用 Lombok 的 @Data 简化) public T getData() { return data; } public void setData(T data) { this.data = data; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
💡 进阶建议:
- 将
code改为int类型(如 200/400/500),更符合 HTTP 语义;- 使用枚举管理常用状态码(如
ResultCode.SUCCESS,ResultCode.PARAM_ERROR);- 后续课程可引入全局异常处理器,自动包装异常为
JsonResult。
3. 在 Controller 中使用统一返回结构
修改之前的 JsonController,返回 JsonResult<T>:
@RestController @RequestMapping("/jsonresult") public class JsonResultController { @RequestMapping("/user") public JsonResult<User> getUser() { User user = new User(1L, "倪升武", "123456"); return new JsonResult<>(user); // 使用有参构造:data + 默认成功提示 } @RequestMapping("/list") public JsonResult<List<User>> getUserList() { List<User> userList = Arrays.asList( new User(1L, "倪升武", "123456"), new User(2L, "达人课", "123456") ); return new JsonResult<>(userList, "获取用户列表成功"); } @RequestMapping("/map") public JsonResult<Map<String, Object>> getMap() { Map<String, Object> map = new HashMap<>(); map.put("作者信息", new User(1L, "倪升武", null)); map.put("博客地址", "http://blog.itcodai.com"); map.put("CSDN地址", null); map.put("粉丝数量", 4153); return new JsonResult<>(map); } }
⚠️ 注意:泛型应尽量具体,如
JsonResult<List<User>>而非JsonResult<List>,便于 Swagger 文档生成和类型安全。
4. 测试统一返回效果
启动项目,访问以下接口:
✅ /jsonresult/user
{ "code": "0", "msg": "操作成功!", "data": { "id": 1, "username": "倪升武", "password": "123456" } }
✅ /jsonresult/list
{ "code": "0", "msg": "获取用户列表成功", "data": [ { "id": 1, "username": "倪升武", "password": "123456" }, { "id": 2, "username": "达人课", "password": "123456" } ] }
✅ /jsonresult/map
{ "code": "0", "msg": "操作成功!", "data": { "作者信息": { "id": 1, "username": "倪升武", "password": "" }, "CSDN地址": "", "粉丝数量": 4153, "博客地址": "http://blog.itcodai.com" } }
🔍 可见:
- 所有接口结构一致;
null已按 Fastjson/Jackson 配置处理;- 前端只需解析
code即可判断业务状态。
5. 最佳实践与扩展方向
| 实践项 | 说明 |
| 状态码标准化 | 定义 ResultCode 枚举,如 SUCCESS(0, "成功"), PARAM_ERROR(1001, "参数错误") |
| 全局异常处理 | 使用 @ControllerAdvice 捕获异常,统一返回 JsonResult.error(...) |
| 分页统一结构 | 可扩展 PageResult<T> 继承 JsonResult,增加 total, pageNum 等字段 |
| Swagger 支持 | 配合 @ApiModel 注解,让 API 文档自动识别返回结构 |
示例:枚举管理状态码
public enum ResultCode { SUCCESS("0", "操作成功!"), PARAM_ERROR("1001", "参数错误"), USER_NOT_FOUND("1002", "用户不存在"); private final String code; private final String msg; // 构造方法、getter... }
6. 总结
本节课我们完成了:
- ✅ 理解统一返回结构的必要性;
- ✅ 设计通用
JsonResult<T>响应类; - ✅ 在 Controller 中应用泛型封装;
- ✅ 验证不同数据类型的返回效果。
统一返回结构是专业 API 设计的基石。它不仅提升了前后端协作效率,也为后续的监控、日志、自动化测试打下基础。
📌 思考题:如果某个接口不需要返回
data(如删除操作),是否还需要返回JsonResult<Void>?欢迎在评论区讨论!