springboot 统一返回体(随笔)

2021-04-19  本文已影响0人  简楼

前言

在我们日常开发中,总是要和APP、H5、PC等多端的进行对接;
此时就需要设计一个合适的接口返回体,以便统一交互;

思路

  1. 定义一个泛型返回体,以此包装所有的接口返回值,让开发只关注业务就行;
  2. 自定义返回体注解,用于标注哪些类或方法需要包装返回值;
  3. ResponseBodyAdvice 实现类,并结合 @ControllerAdvice 注解,实现正常返回值或异常情况时返回值的封装;

定义一个泛型返回体

@Data
public class Response<T> implements Serializable {

    private static final long serialVersionUID = -1220656299702215742L;
    private String code;
    private String message;
    private T data;

    public static <T> Response ok(T data) {
        return new Response("200", "success", data);
    }

    public static <T> Response ok(String code, String message, T data) {
        return new Response(code, message, data);
    }

    public static <T> Response fail(T data) {
        return new Response("500", "fail request", data);
    }

    public static <T> Response fail(String code, String message, T data) {
        return new Response(code, message, data);
    }

    private Response(String code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }
}

这里,只是简单的定义了返回体,有兴趣的同学,可以了解下枚举+返回体的操作;
返回体中,我们只定义了三个变量:

code:返回码
message:消息
data:返回的具体数据

这里我们还把构造私有化了,这里时防止调用的时候有人通过 new 的方式包装数据,这不符合我们一开始定义的规范,需要通过我们定义好的静态方法实现返回值的包装;

自定义返回体注解

@Target({TYPE, METHOD})
@Retention(RUNTIME)
@Documented
public @interface ResponseResult {
}

这个就不需要多解释了,声明这个注解可以使用在类和方法上;

ResponseBodyAdvice 实现类

@Slf4j
@ResponseBody
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {

  /**
   * 判断是否包含 @ResponseResult 注解,不包含直接返回,不重写返回体
   *
   * @param returnType
   * @param converterType
   * @return boolean
   */
  @Override
  public boolean supports(
      MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
    Class<?> clazz = returnType.getExecutable().getDeclaringClass();
    return clazz.isAnnotationPresent(ResponseResult.class) ? Boolean.TRUE : Boolean.FALSE;
  }

  /**
   * 返回体重构
   *
   * @param body
   * @param returnType
   * @param selectedContentType
   * @param selectedConverterType
   * @param request
   * @param response
   * @return java.lang.Object
   */
  @Override
  public Object beforeBodyWrite(
      Object body,
      MethodParameter returnType,
      MediaType selectedContentType,
      Class<? extends HttpMessageConverter<?>> selectedConverterType,
      ServerHttpRequest request,
      ServerHttpResponse response) {
    log.info("重写结构。。。。");
    return Response.ok(body);
  }

  /**
   * 自定义异常拦截
   *
   * @param e
   * @return com.example.demo.utils.Response
   */
  @ExceptionHandler(MyException.class)
  public Response getException(MyException e) {
    log.error("自定义异常:{},{}", e.getCode(), e.getMessage());
    return Response.fail(e.getCode(), e.getMessage(), null);
  }

  /**
   * 最大异常拦截
   *
   * @param e
   * @return com.example.demo.utils.Response
   */
  @ExceptionHandler(Exception.class)
  public Response getException(Exception e) {
    log.error("异常:{}", e);
    return Response.fail(e.getMessage());
  }
}

重写 supports 和 beforeBodyWrite 方法,以及拦截异常;

通过阅读 ResponseBodyAdvice 源码中的 supports 和 beforeBodyWrite 方法,可以知道,当 supports 返回
true 时,才会执行 beforeBodyWrite方法;

supports 方法中可以通过 MethodParameter 来获取类或方法上的注解,借此判断注解上是否有我们自定义的 @ResponseResult注解,如果有的话,就返回true,否则就是false;

beforeBodyWrite 将返回值包装到我们自定的返回体中;

拦截异常,就是 @ExceptionHandler 注解中指定异常类型,那么该方法就会拦截你指定的异常,拦截后就会执行我们自定义的异常处理逻辑,最后返回时,使用我们自定的返回体封装结果,就万事大吉了;

注意:拦截异常时,需要有一个最大的异常兜底,因为没有最大异常兜底的话,那么当系统抛出一个拦截异常中最大异常还大的异常,就会捕捉不到,那么我们设置的异常拦截,那将毫无意义;

总结

小伙伴们,最好亲自动手,写一写,总会有不一样的收获,比如 你的最爱--BUG!!!

上一篇下一篇

猜你喜欢

热点阅读