Spring Boot 统一返回数据格式
在构建 RESTful API 时,保持一致的返回数据格式至关重要。统一的返回格式不仅可以提高 API 的可读性,还能方便客户端解析和处理响应数据。Spring Boot 提供了多种方式来实现统一的返回数据格式,本文将深入探讨如何在 Spring Boot 项目中实现这一目标,并提供代码示例和最佳实践。
为什么需要统一的返回数据格式?
- 提高 API 可读性: 统一的格式可以让客户端更容易理解 API 的响应数据。
- 简化客户端解析:客户端可以编写更简单的代码来解析和处理响应数据,无需处理各种不同的数据结构。
- 方便错误处理:统一的格式可以方便客户端识别错误,并根据错误代码进行相应处理。
- 提升 API 的整体质量: 一致的数据格式是良好 API设计的基本要素,可以提高 API 的整体质量和易用性。
- 方便前后端协同: 统一的数据格式可以减少前后端开发人员在数据对接过程中的沟通成本,提高开发效率。
Spring Boot 中实现统一返回数据格式的思路
在 Spring Boot 中,实现统一的返回数据格式,通常采用以下步骤:
1.定义统一的响应体结构: 创建一个 Java 类,用于表示统一的响应体结构,该结构通常包含:
code
: 表示响应状态码,例如成功、失败、参数错误等。
message
: 表示响应消息,用于描述请求结果。
data
: 表示响应数据,用于返回实际的数据内容。
timestamp
: (可选) 返回时间戳。
2.创建统一的响应类:创建一个工具类,用于创建具有统一格式的响应对象。
使用 @RestControllerAdvice
或 @ResponseBodyAdvice
进行全局处理:
使用 @RestControllerAdvice
注解可以拦截所有 @RestController
中的响应,并修改响应数据。
使用 @ResponseBodyAdvice
注解也可以拦截响应,但是需要配合 @Controller
使用。
实践:创建统一的响应数据格式
1.定义统一的响应体结构
@ApiModel("响应结果")
@Data
public class Result<T> extends BaseObject{
/**
* code
*/
@ApiModelProperty(value = "响应代码")
private Integer code;
/**
* message
*/
@ApiModelProperty(value = "响应消息")
private String msg;
/**
* data
*/
@ApiModelProperty(value = "数据结果")
private T data;
public Result() {
}
public Result(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
private Result(T data) {
this.code = Status.SUCCESS.getCode();
this.data = data;
}
private Result(Status status) {
if (status != null) {
this.code = status.getCode();
this.msg = status.getMsg();
}
}
public Result(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
/**
* Call this function if there is success
* @param data data
* @param <T> type
* @return resule
*/
public static <T> Result<T> success(T data) {
return new Result<>(data);
}
/**
* 返回错误消息
* @return
*/
public static Result error()
{
return Result.error("操作失败");
}
/**
* 返回错误消息
* @param msg 返回内容
* @return 警告消息
*/
public static Result error(String msg)
{
return Result.error(msg, null);
}
}
2.定义响应枚举 (ResultCode):
public enum Status {
//成功
SUCCESS(0, "success", "成功"),
//系统异常
SYSTEM_EXCEPTION(10000, "system exception", "系统异常"),
//业务异常
SERVICE_EXCEPTION(10001, "service exception", "业务异常")
;
private final int code;
private final String enMsg;
private final String zhMsg;
private Status(int code, String enMsg, String zhMsg) {
this.code = code;
this.enMsg = enMsg;
this.zhMsg = zhMsg;
}
public int getCode() {
return this.code;
}
public String getMsg() {
if (Locale.SIMPLIFIED_CHINESE.getLanguage().equals(LocaleContextHolder.getLocale().getLanguage())) {
return this.zhMsg;
} else {
return this.enMsg;
}
}
}
3.定义全局统一响应处理类(可选):
@ControllerAdvice
public class CustomerResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
return o;
}
}
- 使用
@RestControllerAdvice
注解声明该类为全局响应处理类。 - 实现了
ResponseBodyAdvice
接口,可以拦截所有的响应。 - 在
beforeBodyWrite
方法中,将返回数据包装成 Result 对象。 supports
方法判断是否需要拦截,这里通过判断包名进行拦截。
4.在 Controller 中直接返回业务数据
@RestController
@RequestMapping("/yes")
public class ExtendController extends BaseController {
@Autowired
private UserInfoService userInfoService;
@PostMapping("/list")
public Result<PageDataInfo<UserInfo>> queryUserInfoList(@RequestBody UserInfoReq req) {
startPage(req);
return Result.success(buildPageDataInfo(userInfoService.list()));
}
}
5.测试统一响应格式
{
"code": 0,
"msg": null,
"data": {
"total": 5,
"list": [
{
"id": 1,
"name": "test02",
"password": "test02",
"age": 1,
"status": 1,
"lastLoginTime": null,
"token": null,
"createTime": "2022-06-21 17:57:14",
"updateTime": "2022-06-21 17:57:16"
}
],
"statObj": null
}
}
最佳实践
- 使用枚举管理状态码: 使用枚举 (ResultCode) 来管理状态码,避免使用 Magic Number,提高代码可读性和可维护性。
- 提供更友好的错误提示: 在全局异常处理中,使用 Result 对象返回更友好的错误提示信息,方便客户端处理。
- 考虑使用 AOP:可以使用 AOP (面向切面编程)来统一处理返回格式,减少代码重复。
- 灵活控制返回格式:可以根据实际需求,添加更多的字段,例如分页信息,响应时间等等。