所有文章 > API开发 > SpringBoot无侵入式实现接口统一返回JSON格式

SpringBoot无侵入式实现接口统一返回JSON格式

现在基本项目都实现统一返回JSON,但需要每个接口都设置,那有没有全局设置呢?今天就介绍如何无侵入式的实现API接口统一JSON格式返回响应

常见方式

1.定义结果码枚举

@Data
public enum ResultCode {

    SUCCESS("0000","成功"),
    FAIL("10001","操作失败");

    /** 结果码 */
    private String code;
    /** 结果码描述 */
    private String message;

    ResultCode(String code, String message) {
        this.code = code;
        this.message = message;
    }
}

2.定义返回结果类

@Data   
public class Result<T> {  
    /** 结果码 */  
    private String code;  
    /** 结果码描述 */  
    private String message;  
    /** 返回参数 */  
    private T data;  
  
    private Result(ResultCode resultCode, T data) {  
        this.code = resultCode.getCode();  
        this.message = resultCode.getMessage();  
        this.data = data;  
    }  
  
    /** 成功返回结果码和描述 */  
    public static Result<Void> success() {  
        return new Result<Void>(ResultCode.SUCCESS, null);  
    }  
  
    /** 成功返回结果码,描述和返回参数 */  
    public static <T> Result<T> success(T data) {  
        return new Result<T>(ResultCode.SUCCESS, data);  
    }  
  
    /** 成功返回结果码,描述和返回参数 */  
    public static <T> Result<T> success(ResultCode resultCode, T data) {  
        if (resultCode == null) {  
            return success(data);  
        }  
        return new Result<T>(resultCode, data);  
    }  
  
    /** 异常返回结果码和描述 */  
    public static <T> Result<T> failure() {  
        return new Result<T>(ResultCode.FAIL, null);  
    }  
  
    /** 异常返回结果码,描述 */  
    public static <T> Result<T> failure(ResultCode resultCode) {  
        return failure(resultCode, null);  
    }  
  
    /** 异常返回结果码,描述和返回参数 */  
    public static <T> Result<T> failure(ResultCode resultCode, T data) {  
        if (resultCode == null) {  
            return new Result<T>(ResultCode.FAIL, data);  
        }  
        return new Result<T>(resultCode, data);  
    }  
}

3.控制层

@RestController
public class HelloController {

    @GetMapping("/test")
    public Result hello() {
        HashMap<String, Object> info = new HashMap<>();
        info.put("name","一安未來");
        info.put("addr","北京");
        return Result.success(ResultCode.SUCCESS,info);
    }
}

4.接口测试

如果还有接口test2、test3、test4…..,即每个接口都必须返回Result对象,这也是目前大多数项目正在使用的方式

优化一

1.继承@ResponseBody

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

2.实现ResponseBodyAdvice

ResponseBodyAdvice 接口允许在执行 @ResponseBody 或 ResponseEntity 控制器方法之后,但在使用 HttpMessageConverter 写入响应体之前自定义响应,进行功能增强。通常用于加密,签名,统一数据格式等

相应的也有RequestBodyAdvice接口,针对所有以@RequestBody的参数,在读取请求body之前或者在body转换成对象之前可以做相应的增强

@Slf4j
@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {

    private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseResultBody.class;

    /**
    * 1、选择是否执行 beforeBodyWrite 方法,返回 true 执行,false 不执行.
    * 2、通过supports方法,可以选择对哪些类或方法的 Response 进行处理,其余的则不处理。
    * @param returnType:返回类型
    * @param converterType:转换器
    * @return :返回 true 则下面的 beforeBodyWrite  执行,否则不执行
    * /
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ANNOTATION_TYPE) || returnType.hasMethodAnnotation(ANNOTATION_TYPE);
    }

    /**
    * 对 Response 处理的具体执行方法
    * @param body:响应对象(response)中的响应体
    * @param returnType:控制器方法的返回类型
    * @param selectedContentType:通过内容协商选择的内容类型
    * @param selectedConverterType:选择写入响应的转换器类型
    * @param request:当前请求
    * @param response:当前响应
    * @return :返回传入的主体或修改过的(可能是新的)主体
   * /
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return convert(convert(body), selectedConverterType);
    }

    private Result<?> convert(Object body) {
        //兼容返回是Result类型
        if (body instanceof Result) {
            return (Result<?>) body;
        }
        return Result.success(body);
    }

    private Object convert(Result<?> result, Class<? extends HttpMessageConverter<?>> selectedConverterType) {
        //兼容返回是String类型
        if (selectedConverterType == StringHttpMessageConverter.class && result.getData() instanceof String) {
            return "{\"code\":\"" + result.getCode() + "\",\"message\":\"" + result.getMessage() + "\",\"data\":\"" + result.getData() + "\"}";
        }
        return result;
    }
}

3.控制层

@RestController
@ResponseResultBody
public class HelloController {

    @GetMapping("/test")
    public Result hello() {
        HashMap<String, Object> info = new HashMap<>();
        info.put("name","一安未來");
        info.put("addr","北京");
        return Result.success(ResultCode.SUCCESS,info);
    }

    @GetMapping("/test2")
    public Object hello2() {
        HashMap<String, Object> info = new HashMap<>();
        info.put("name","一安未來");
        info.put("addr","北京");
        return info;
    }
}

4.接口测试

至此,即使返回Object也可以统一JSON格式了, 不用每个返回都返回Result对象

优化二

使用@ExceptionHandler可以用来统一处理方法抛出的异常返回JSON格式

自定义异常处理类

@Data
public class ResultException extends Exception{

    ResultCode resultCode;

    public ResultException() {
        this(ResultCode.FAIL);
    }

    public ResultException(ResultCode resultCode) {
        super(resultCode.getMessage());
        this.resultCode = resultCode;
    }
}

改造ResponseResultBodyAdvice

ResponseBodyAdvice 接口允许在执行 @ResponseBody 或 ResponseEntity 控制器方法之后,但在使用 HttpMessageConverter 写入响应体之前自定义响应,进行功能增强。通常用于加密,签名,统一数据格式等

相应的也有RequestBodyAdvice接口,针对所有以@RequestBody的参数,在读取请求body之前或者在body转换成对象之前可以做相应的增强

@Slf4j
@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {

    private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseResultBody.class;

    /**
    * 1、选择是否执行 beforeBodyWrite 方法,返回 true 执行,false 不执行.
    * 2、通过supports方法,可以选择对哪些类或方法的 Response 进行处理,其余的则不处理。
    * @param returnType:返回类型
    * @param converterType:转换器
    * @return :返回 true 则下面的 beforeBodyWrite执行,否则不执行
    * /
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ANNOTATION_TYPE) || returnType.hasMethodAnnotation(ANNOTATION_TYPE);
    }

    /**
    * 对 Response 处理的具体执行方法
    * @param body:响应对象(response)中的响应体
    * @param returnType:控制器方法的返回类型
    * @param selectedContentType:通过内容协商选择的内容类型
    * @param selectedConverterType:选择写入响应的转换器类型
    * @param request:当前请求
    * @param response:当前响应
    * @return :返回传入的主体或修改过的(可能是新的)主体
   * /
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return convert(convert(body), selectedConverterType);
    }

    private Result<?> convert(Object body) {
        if (body instanceof Result) {
            return (Result<?>) body;
        }
        return Result.success(body);
    }

    private Object convert(Result<?> result, Class<? extends HttpMessageConverter<?>> selectedConverterType) {
        if (selectedConverterType == StringHttpMessageConverter.class && result.getData() instanceof String) {
            return "{\"code\":\"" + result.getCode() + "\",\"message\":\"" + result.getMessage() + "\",\"data\":\"" + result.getData() + "\"}";
        }
        return result;
    }


    /**
     * 提供对标准Spring MVC异常的处理
     */
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Result<?>> exceptionHandler(Exception ex, WebRequest request) {
        log.error("ExceptionHandler: {}", ex.getMessage());
        HttpHeaders headers = new HttpHeaders();
        if (ex instanceof ResultException) {
            return this.handleResultException((ResultException) ex, headers, request);
        }
        // TODO:  这里可以自定义其他的异常拦截
        return this.handleException(ex, headers, request);
    }

    /**
     * 对ResultException类返回返回结果的处理
     */
    protected ResponseEntity<Result<?>> handleResultException(ResultException ex, HttpHeaders headers, WebRequest request) {
        Result<?> body = Result.failure(ex.getResultCode());
        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    /**
     * 异常类的统一处理
     */
    protected ResponseEntity<Result<?>> handleException(Exception ex, HttpHeaders headers, WebRequest request) {
        Result<?> body = Result.failure();
        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        return this.handleExceptionInternal(ex, body, headers, status, request);
    }

    protected ResponseEntity<Result<?>> handleExceptionInternal(
            Exception ex, Result<?> body, HttpHeaders headers, HttpStatus status, WebRequest request) {

        if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
            request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
        }
        return new ResponseEntity<>(body, headers, status);
    }
}

文章转自微信公众号@一安未来

#你可能也喜欢这些API文章!