在Spring Boot中,统一返回前端格式可以提高代码复用性和维护性。本文将介绍如何在Spring Boot项目中封装统一的响应格式。
响应格式的设计
在设计响应格式时,可以参考RESTful API的设计规范,通常一个合理的响应格式应该包含以下几个字段:
code
:响应状态码,用于表示请求的处理结果。一般情况下,2xx表示成功(个人习惯用0代表成功),4xx表示客户端错误,5xx表示服务端错误。message
:响应消息,用于描述响应状态码的具体含义。data
:响应数据,用于存放请求处理成功后的数据。如果请求失败,则该字段可以为空或者不存在。
在具体的实现中,data
字段可以是一个对象,也可以是一个集合。另外,为了方便前端的解析,响应格式通常采用JSON格式。
基于上述规范,我们可以定义一个通用的响应实体类:
@Data
public class DataResult <T>{
@ApiModelProperty(value = "响应状态码 code=0代表成功,其它失败")
private int code;
@ApiModelProperty(value = "响应的业务数据")
private T data;
@ApiModelProperty(value = "响应状提示语")
private String message;
/**
* 响应成功的方法
* @param data
* @param <T>
* @return
*/
public static <T> DataResult<T> success(T data){
DataResult<T> result=new DataResult<>();
result.data=data;
result.code= ResponseCode.SUCCESS.getCode();
result.message=ResponseCode.SUCCESS.getMessage();
return result;
}
/**
* 响应失败工具方法
* @param code
* @param message
* @param <T>
* @return
*/
public static <T> DataResult<T> fail(int code ,String message){
DataResult<T> result=new DataResult<>();
result.code= code;
result.message=message;
return result;
}
}
复制代码
定义一个统一响应状态码枚举:
public enum ResponseCode {
SUCCESS(0,"响应成功"),
//后端服务异常以500开头
SYSTEM_ERROR(500000,"服务异常,请稍后再试"),
OPERATION_ERROR(500001,"操作失败,请稍后再试"),
//后端服务异常以400开头
DATA_PARAM_ERROR(400000,"传入参数错误"),
ACCOUNT_ALREADY_EXISTS(400001,"账号已存在,请登录"),
ACCOUNT_NOT_FOUND(400002,"账号不存在"),
ACCOUNT_LOCK(400003,"账号已锁定,请联系管理员解锁"),
ACCOUNT_ERROR(400004,"账户密码不匹配"),
TOKEN_ERROR(401000,"token 已失效,请重新登录")
;
private final int code;
private final String message;
ResponseCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
复制代码
统一响应格式的封装
我们可以使用Spring提供的ResponseBodyAdvice
来实现统一响应格式的封装。具体来说,ResponseBodyAdvice
是一个接口,可以在Controller方法执行完成后,对返回的结果进行修改和增强。
首先,我们需要定义一个通用的响应格式类:
@RestControllerAdvice
@Slf4j
public class RestResponseBodyAdviceHandler implements ResponseBodyAdvice<Object> {
@Resource
private ObjectMapper objectMapper;
private final String stringConverter="org.springframework.http.converter.StringHttpMessageConverter";
/**
* true:代表支持我们在响应前端的时候做一些处理(调用beforeBodyWrite方法)
* false:不支持
* @param returnType
* @param converterType
* @return
*/
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
log.info("supports:{}",returnType.getDeclaringClass().getName());
/**
* 排除swagger-ui请求返回数据增强
*/
return !returnType.getDeclaringClass().getName().contains("springfox");
}
@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
/**
* 当接口返回到类型消息转换器是StringHttpMessageConverter
* 我们才需要把它转换成string
*/
if(stringConverter.equalsIgnoreCase(selectedConverterType.getName())){
HttpHeaders headers= response.getHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return objectMapper.writeValueAsString(DataResult.success(body));
}
/**
*
* 如果响应结果已经是DataResult类型,则直接返回
*/
if(body instanceof DataResult){
return body;
}
return DataResult.success(body);
}
}
复制代码
在上述代码中,我们实现了ResponseBodyAdvice
接口,并重写了其中的supports
和beforeBodyWrite
方法。其中,supports
方法用于判断当前返回类型是否需要进行处理,beforeBodyWrite
方法则用于对返回结果进行封装
在Spring Boot中,StringHttpMessageConverter
消息转换器已经默认添加到了消息转换器列表中,它会将请求和响应中的字符串类型数据转换成Java中的String
类型,当接口返回类型是字符串类型,则StringHttpMessageConverter
会处理该请求,并将请求中的数据转换成Java中的String
类型,如果我们直接返回到是DataResult 类型就会出现类型转换异常:xxxxx.DataResult cannot be cast to java.lang.String
当接口返回类型为DataResult
时已经是 DataResult
类型了所以我们不需要处理,反之需要使用 DataResult
包装处理统一返回DataResult
类型。
使用ResponseBodyAdvice
的好处是,它可以更加精细地控制响应结果的格式,可以对所有的返回值进行统一的封装,也可以针对某些返回值进行特殊处理,灵活性更高。
测试
@RestController
@Api(tags = "测试模块")
public class TestController {
@GetMapping("/hello")
@ApiOperation(value = "经典hello接口")
public String hello(){
return "Hello World";
}
}
复制代码
结果:
{
"code": 0,
"data": "Hello World",
"message": "响应成功"
}
复制代码