SpringBoot 项目中统一返回前端格式老手是这样封装的

在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接口,并重写了其中的supportsbeforeBodyWrite方法。其中,supports方法用于判断当前返回类型是否需要进行处理,beforeBodyWrite方法则用于对返回结果进行封装

在Spring Boot中,StringHttpMessageConverter消息转换器已经默认添加到了消息转换器列表中,它会将请求和响应中的字符串类型数据转换成Java中的String类型,当接口返回类型是字符串类型,则StringHttpMessageConverter会处理该请求,并将请求中的数据转换成Java中的String类型,如果我们直接返回到是DataResult 类型就会出现类型转换异常:xxxxx.DataResult cannot be cast to java.lang.String

1683185247117.png

当接口返回类型为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": "响应成功"
}
复制代码

猜你喜欢

转载自juejin.im/post/7229217442424717370