当前位置: 首页 > news >正文

Spring Boot:统一返回格式,这样搞就对了。

Spring统一数据返回格式的核心思想是:强制所有Controller的返回值遵循同一个JSON结构,从而使前端处理响应时有一致的规范。


为什么要统一数据返回格式?

  1. 前端处理方便:前端不需要为每个接口判断不同的响应结构,只需处理一种固定格式。
  2. 便于协作:前后端约定好格式后,可以并行开发。
  3. 标准化:包含状态码、信息、数据等标准字段,易于理解和维护。
  4. 易于扩展:可以统一添加如timestamppath等通用字段。

标准的统一响应体格式

一个通用的响应体通常包含以下字段:

字段名类型必须说明
codeInteger业务状态码(或直接用HTTP状态码)。例如:200成功,500服务器错误。
messageString对本次响应的提示信息,如“操作成功”、“用户不存在”等。
dataObject返回的有效负载数据,可以是任意JSON类型(对象、数组、字符串等)。
timestampLong响应时间戳(毫秒),便于调试和记录。

JSON示例:

成功响应:

{"code": 200,"message": "请求成功","data": {"id": 1,"name": "张三","age": 25},"timestamp": 1719371635110
}

失败/异常响应:

{"code": 500,"message": "系统内部异常,请联系管理员","data": null,"timestamp": 1719371635110
}

实现方案(三种,推荐方案三)

方案一:手动封装(不推荐,繁琐)

在每个Controller方法中手动创建响应对象。

// 1. 首先定义一个通用的响应类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {private Integer code;private String message;private T data;private Long timestamp = System.currentTimeMillis();public static <T> Result<T> success(T data) {return new Result<>(200, "成功", data, System.currentTimeMillis());}public static <T> Result<T> error(Integer code, String message) {return new Result<>(code, message, null, System.currentTimeMillis());}
}// 2. 在Controller中手动使用
@RestController
@RequestMapping("/user")
public class UserController {@GetMapping("/{id}")public Result<User> getUser(@PathVariable Long id) {User user = userService.getById(id);return Result.success(user); // 手动包装}@PostMappingpublic Result<String> createUser(@RequestBody User user) {userService.save(user);return Result.success("用户创建成功"); // 手动包装}
}

缺点:每个方法都要写Result.success(...),代码重复。


方案二:使用@ControllerAdvice注解(推荐,优雅)

通过实现ResponseBodyAdvice接口,在数据写入Response Body之前进行拦截和包装。

步骤:

  1. 定义统一响应体Result(同上)。
  2. 实现ResponseBodyAdvice
@RestControllerAdvice(basePackages = "com.yourpackage.controller") // 指定要拦截的包
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {/*** 判断是否支持advice功能* 返回true表示支持,会执行beforeBodyWrite方法*/@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {// 排除掉已经封装成Result的返回值、Swagger的响应等return !returnType.getParameterType().isAssignableFrom(Result.class) &&!returnType.hasMethodAnnotation(ResponseBody.class); // 通常不需要这个检查,但根据情况定}/*** 对响应体进行实际包装*/@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {// 如果返回的是String类型,需要特殊处理(因为String的转换器不同)if (body instanceof String) {// 通常不会直接返回String,如果返回了,需要手动转成JSON// 实际开发中应避免Controller直接返回Stringreturn JSON.toJSONString(Result.success(body));}// 如果返回体已经是Result类型(比如全局异常处理器返回的),则直接返回if (body instanceof Result) {return body;}// 最普遍的情况:包装成Result格式return Result.success(body);}
}
  1. Controller保持干净
@RestController
@RequestMapping("/user")
public class UserController {// 直接返回数据对象,GlobalResponseHandler会自动帮你包装成Result格式@GetMapping("/{id}")public User getUser(@PathVariable Long id) {return userService.getById(id);}@PostMappingpublic String createUser(@RequestBody User user) {userService.save(user);return "用户创建成功"; // 不推荐直接返回String,容易出问题}// 返回null也可以@DeleteMapping("/{id}")public void deleteUser(@PathVariable Long id) {userService.removeById(id);}
}

注意:直接返回String类型可能会引发异常,因为Spring有专门的StringHttpMessageConverter。建议避免Controller直接返回String,或者在你的beforeBodyWrite方法中做好特殊处理(如上例所示)。


方案三:结合全局异常处理(最完整方案)

通常,方案二会和全局异常处理一起使用,以统一成功和失败的格式。

@RestControllerAdvice
public class GlobalExceptionHandler {/*** 捕获所有未知异常*/@ExceptionHandler(Exception.class)public Result<String> handleException(Exception e) {// 记录日志...return Result.error(500, "系统繁忙,请稍后再试: " + e.getMessage());}/*** 捕获业务异常(假设你有一个自定义的BusinessException)*/@ExceptionHandler(BusinessException.class)public Result<String> handleBusinessException(BusinessException e) {return Result.error(e.getCode(), e.getMessage());}
}

这样,无论是正常响应还是异常抛出,最终返回给前端的都是统一的Result格式。


总结

方案优点缺点推荐度
手动封装简单直观,控制灵活代码重复,容易遗漏⭐⭐
@ControllerAdvice自动化,代码整洁,非侵入式需要处理String类型的特殊情况⭐⭐⭐⭐⭐
结合异常处理最完整的方案,统一所有响应配置稍复杂⭐⭐⭐⭐⭐

最佳实践:采用方案三(@ControllerAdvice + 全局异常处理),这是目前Spring Boot项目中实现统一返回格式最主流、最优雅的方式。

http://www.dtcms.com/a/352677.html

相关文章:

  • HMM简单拓展-HSMM与高阶HMM
  • 视频号存在争议了...
  • 软件开发技术栈
  • JVM之【运行时数据区】
  • 深度学习-----ptorch框架认识-手写数字识别.py项目解读
  • 2025年渗透测试面试题总结-34(题目+回答)
  • three.js+WebGL踩坑经验合集(9.2):polygonOffsetFactor工作原理大揭秘
  • Langchian-chatchat私有化部署和踩坑问题以及解决方案[v0.3.1]
  • More Effective C++ 条款10:在构造函数中防止资源泄漏
  • 二维费用背包 分组背包
  • 小范围疫情防控元胞自动机模拟matlab
  • 深入剖析容器文件系统:原理、实现与资源占用分析
  • 游戏空间划分技术
  • 家庭财务规划与投资系统的设计与实现(代码+数据库+LW)
  • 声网RTC稳定连麦、超分清晰,出海直播技术不再难选
  • AT_abc403_f [ABC403F] Shortest One Formula
  • 【44页PPT】极简架构MES系统解决方案介绍(附下载方式)
  • 【Python】雷达簇类ply点云仿真生成,以及聚类算法的簇类目标检测
  • flutter专栏--dart基础知识
  • WebGIS开发智慧校园(6)JavaScript
  • 破解VMware迁移难题的技术
  • SSH密钥登录全流程详解
  • LeetCode-221. 最大正方形
  • 多模块 Starter 最佳实践(强烈推荐!!!)
  • Quarkus OIDC 安全链路时序图
  • git换行行为差异简述;.editorconfig换行行为简述
  • 打工人日报#20250826
  • 【PS实战】制作hello标志设计:从选区到色彩填充的流程(大学作业)
  • springboot启动的时候,只打印logo,不打印其他的任何日志的原因
  • 【ElasticSearch】数据同步