前言
在我们日常开发中,总是要和APP、H5、PC等多端的进行对接;
此时就需要设计一个合适的接口返回体,以便统一交互;
思路
- 定义一个泛型返回体,以此包装所有的接口返回值,让开发只关注业务就行;
 - 自定义返回体注解,用于标注哪些类或方法需要包装返回值;
 - 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;
    }
}
这里,只是简单的定义了返回体,有兴趣的同学,可以了解下枚举+返回体的操作;
返回体中,我们只定义了三个变量:
这里我们还把构造私有化了,这里时防止调用的时候有人通过 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!!!










