SpringBoot之统一接口返回后端结果格式

在实际开发中,每个接口的返回格式是不一致的,对应前端接收和处理不方便。
而且抛出异常通过try

目录

  • 一、设计思路
  • 二、结果处理
    • 2.1 定义返回对象
    • 2.2 全局响应处理器
  • 三、自定义IgnoreResponseAdvice注解
    • 3.1 创建自定义注解
    • 3.2 实现注解功能
  • 四、异常处理
    • 4.1 全局异常处理器
    • 4.2 重复封装问题

一、设计思路

  1. 只对controller层返回的结果进行保证处理,业务逻辑处理的结果不进行处理
  2. 对于不想进行结果封装的接口,通过自定义注解IgnoreResponseAdvice让其不封装;
  3. 对于异常,进行全局处理,然后封装成对应的结果

二、结果处理

2.1 定义返回对象

构造方法私有化,通过静态方法构建对应的返回对象

public class HttpResult<T> implements Serializable {
    /** 是否响应成功 */
    private Boolean success;
    /** 响应状态码 */
    private Integer code;
    /** 响应数据 */
    private T data;
    /** 错误信息 */
    private String message;

    /**
     * 无参构造器(构造器私有,外部不可以直接创建)
     */
    private HttpResult() {
        this.code = 200;
        this.success = true;
    }
    /**
     * 有参构造器
     * @param obj
     */
    private HttpResult(T obj) {
        this.code = 200;
        this.data = obj;
        this.success = true;
    }

    /**
     * 有参构造器
     * @param resultCode
     */
    private HttpResult(ResultCodeEnum resultCode) {
        this.success = false;
        this.code = resultCode.getCode();
        this.message = resultCode.getMessage();
    }
    // 构造器结束

    /**
     * 通用返回成功(没有返回结果)
     * @param 
     * @return
     */
    public static<T> HttpResult<T> success(){
        return new HttpResult();
    }

    /**
     * 返回成功(有返回结果)
     * @param data
     * @param 
     * @return
     */
    public static<T> HttpResult<T> success(T data){
        return new HttpResult<T>(data);
    }

    /**
     * 通用返回失败
     * @param resultCode
     * @param 
     * @return
     */
    public static<T> HttpResult<T> failure(ResultCodeEnum resultCode){
        return  new HttpResult<T>(resultCode);
    }

    public Boolean getSuccess() {
        return success;
    }

    public void setSuccess(Boolean success) {
        this.success = success;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return "HttpResult{" +
                "success=" + success +
                ", code=" + code +
                ", data=" + data +
                ", message='" + message + '\'' +
                '}';
    }

}

2.2 全局响应处理器

通过SpringBoot提供的ResponseBodyAdvice实现对全局的controller的响应结果进行处理。

ResponseBodyAdvice的作用:拦截Controller方法的返回值,统一处理返回值/响应体,一般用来统一返回格式,加解密,签名等等

@RestControllerAdvice
public class ResponseHandler implements ResponseBodyAdvice<Object> {


    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        //TODO: 对响应进行处理,执行 beforeBodyWrite 方法\
        return true;
    }

    /**
     * @author: wujt
     * @date: 2022/7/11
     * @Title: beforeBodyWrite
     * @Description : 对返回结果进行封装
     * @param body
     * @param returnType
     * @param selectedContentType
     * @param selectedConverterType
     * @param request
     * @param response
     * @return java.lang.Object
     */
    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if(body instanceof String){
            return body;
        }
        return HttpResult.success(body);
    }
}

三、自定义IgnoreResponseAdvice注解

3.1 创建自定义注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreResponseAdvice {
}

3.2 实现注解功能

前面全局响应处理器中有support方法,只有为true时才进行处理。
通过反射,让方法或者类有有IgnoreResponseAdvice注解时,返回false,不进行处理。

@Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        //当前类使用IgnoreResponseAdvice注解,不进行处理
        if (returnType.getDeclaringClass().isAnnotationPresent(
                IgnoreResponseAdvice.class
        )) {
            return false;
        }

        //当前方法使用IgnoreResponseAdvice注解,不进行处理
        if (returnType.getMethod().isAnnotationPresent(
                IgnoreResponseAdvice.class
        )) {
            return false;
        }
        //TODO: 对响应进行处理,执行 beforeBodyWrite 方法\
        return true;
    }

四、异常处理

4.1 全局异常处理器

也是通过ResponseBodyAdvice实现对全局的controller的响应结果进行处理,不同的是,新增了**@ExceptionHandler**注解。

@ExceptionHandler:统一处理某一类异常,从而减少代码重复率和复杂度

打印日志内容,除了异常内容还加了getStackTrace(),如果只是单纯异常内容,无法定位是从那个方法那一行出现了问题,因为被全局异常处理器拦截,统一都在异常处理器这里打印了。

@RestControllerAdvice
@Slf4j
public class ExceptionHandler {

    /**
     * @author: wujt
     * @date: 2022/7/11
     * @Title: handlerException
     * @Description : 对捕获的异常进行处理和封装返回结果
     * @param e
     * @return com.southgis.ibase.infosvr.common.other.constant.entity.HttpResult
     */
    @ExceptionHandler(Exception.class)
    public HttpResult handlerException(Exception e){
        ResultCodeEnum resultCodeEnum = ResultCodeEnum.SERVER_ERROR;
        resultCodeEnum.setMessage(e.getMessage());
        log.error("common exception:{}",e.toString()+"\n"+ Arrays.asList(e.getStackTrace()).toString());
        return HttpResult.failure(resultCodeEnum);
    }
}

4.2 重复封装问题

在上面通过捕获异常后进行封装,然后系统还会进行结果的封装,导致重复封装。
通过封装时,根据结果类型判断是否封装:

@Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //当结果已经是封装过的,不再进行二次封装(主要是处理异常封装导致重复封装)
        if(body instanceof HttpResult){
        	//根据业务需求需要设计对应的响应码
        	if(!body.getSuccess()){
        		response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        	}
            return body;
        }
        if(body instanceof String){
            return body;
        }
        return HttpResult.success(body);
    }

你可能感兴趣的:(Spring,spring,boot,java,后端)