In the SpringBoot project, the unified return front-end format veteran is encapsulated in this way

In Spring Boot, unified return to the front-end format can improve code reusability and maintainability. This article will introduce how to encapsulate a unified response format in a Spring Boot project.

Responsive Format Design

When designing the response format, you can refer to the design specifications of the RESTful API. Usually, a reasonable response format should contain the following fields:

  • code: Response status code, used to indicate the processing result of the request. Under normal circumstances, 2xx means success (0 means success in personal habits), 4xx means client error, and 5xx means server error.
  • message: Response message, used to describe the specific meaning of the response status code.
  • data: Response data, which is used to store the data after the request is successfully processed. This field can be empty or absent if the request failed.

In a specific implementation, dataa field can be an object or a collection. In addition, in order to facilitate the analysis of the front end, the response format usually adopts JSON format.

Based on the above specifications, we can define a generic response entity class:

@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;
    }
}
复制代码

Define a unified response status code enumeration:

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;
    }
}
复制代码

Encapsulation of unified response format

We can use the encapsulation provided by Spring ResponseBodyAdviceto realize the unified response format. Specifically, ResponseBodyAdviceit is an interface that can modify and enhance the returned result after the Controller method is executed.

First, we need to define a generic response format class:

@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);
    }
}
复制代码

In the above code, we have implemented ResponseBodyAdvicethe interface and rewritten supportsthe sum beforeBodyWritemethod in it. Among them, supportsthe method is used to judge whether the current return type needs to be processed, and beforeBodyWritethe method is used to encapsulate the returned result

在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": "响应成功"
}
复制代码

Guess you like

Origin juejin.im/post/7229217442424717370