( 二十六 )Spring Boot 无侵入式 实现API接口统一JSON格式返回

( 二十六 ) Spring Boot 无侵入式 实现API接口统一JSON格式返回

 

定义JSON格式

后端返回给前端一般情况下使用JSON格式, 定义如下:

{
    "code": 200,
    "message": "OK",
    "data": {

    }
}

 

定义状态码接口(方便扩展)、枚举类

/**
 * @Author
 * @ClassName IResultStatus
 * @Description 返回结果状态信息接口、方便后面扩展
 * @Date 2021/3/31 14:07
 * @Version 1.0
 */
public interface IResultStatus {

    /**
     * 业务状态码
     * @return Integer
     */
     Integer code();

    /**
     * 业务信息描述
     * @return String
     */
     String message();
}

 

枚举类:

/**
 * @Author
 * @ClassName ResultStatus
 * @Description 返回结果状态码枚举类
 * @Date 2021/3/31 14:02
 * @Version 1.0
 */
public enum ResultStatus implements IResultStatus {

    /**
     * 成功
     */
    SUCCESS(200, "OK"),
    /**
     * 错误的请求,参数异常
     */
    BAD_REQUEST(400, "Bad Request params exception"),
    /**
     * 服务器异常
     */
    INTERNAL_SERVER_ERROR(500, "Internal Server Error"),

    /**
     * 401 未认证异常
     */
    UNAUTHORIZED(401, "认证异常"),


    /**
     * 业务异常
     */
    BUSINESS_ERR(10000, "业务异常");


    /**
     * 业务状态码
     */
    private Integer code;

    /**
     * 业务信息描述
     */
    private String message;

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

    /**
     * 业务状态码
     *
     * @return Integer
     */
    @Override
    public Integer code() {
        return code;
    }

    /**
     * 业务信息描述
     *
     * @return String
     */
    @Override
    public String message() {
        return message;
    }
}

 

定义返回体类

/**
 * @Author
 * @ClassName AutoPackageResult
 * @Description
 * @Date 2021/3/31 14:01
 * @Version 1.0
 */
@Data
public class Result<T> {
    /**
     * 业务错误码
     */
    private Integer code;
    /**
     * 信息描述
     */
    private String message;
    /**
     * 返回参数
     */
    private T data;

    private Result(IResultStatus resultStatus, T data) {
        this.code = resultStatus.code();
        this.message = resultStatus.message();
        this.data = data;
    }

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


    /**
     * 业务成功返回业务代码和描述信息
     */
    public static Result<Void> success() {
        return new Result<Void>(ResultStatus.SUCCESS, null);
    }

    /**
     * 业务成功返回业务代码,描述和返回的参数
     */
    public static <T> Result<T> success(T data) {
        return new Result<T>(ResultStatus.SUCCESS, data);
    }

    /**
     * 业务成功返回业务代码,描述和返回的参数
     */
    public static <T> Result<T> success(IResultStatus resultStatus, T data) {
        if (resultStatus == null) {
            return success(data);
        }
        return new Result<T>(resultStatus, data);
    }

    /**
     * 业务成功返回业务代码,描述和返回的参数
     */
    public static <T> Result<T> success(Integer code, String message, T data) {
        return new Result<T>(code, message, data);
    }


    /**
     * 业务异常返回业务代码和描述信息
     */
    public static <T> Result<T> fail() {
        return new Result<T>(ResultStatus.BUSINESS_ERR, null);
    }

    /**
     * 业务异常返回业务代码和描述信息
     */
    public static <T> Result<T> fail(String message) {
        return new Result<T>(ResultStatus.BUSINESS_ERR.code(), message, null);
    }

    /**
     * 业务异常返回业务代码,描述和返回的参数
     */
    public static <T> Result<T> fail(IResultStatus resultStatus) {
        return fail(resultStatus, null);
    }

    /**
     * 业务异常返回业务代码,描述和返回的参数
     */
    public static <T> Result<T> fail(IResultStatus resultStatus, String message) {
        if (resultStatus == null) {
            return new Result<T>(ResultStatus.BUSINESS_ERR, null);
        }
        return new Result<T>(resultStatus.code(), message, null);
    }
}

 

因为使用构造方法进行创建对象太麻烦了, 我们使用静态方法来创建对象这样简单明了

到这里我们已经简单的实现了统一JSON格式了, 但是我们也发现了一个问题了,想要返回统一的JSON格式需要返回Result<Object>才可以, 我明明返回Object可以了, 为什么要重复劳动, 有没有解决方法, 当然是有的啦, 下面我们开始优化我们的代码吧

统一返回JSON格式进阶-全局处理(@RestControllerAdvice)

我们都知道使用@ResponseBody注解会把返回Object序列化成JSON字符串,就先从这个入手吧, 大致就是在序列化前把Object赋值给Result<Object>就可以了, 大家可以观摩org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice 和   org.springframework.web.bind.annotation.ResponseBody

@ResponseBody继承类

我们已经决定从@ResponseBody注解入手了就创建一个注解类继承@ResponseBody, 很干净什么都没有哈哈,
@ResponseResultBody 可以标记在类和方法上这样我们就可以跟自由的进行使用了

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

}

 

ResponseBodyAdvice继承类

/**
 * @Author
 * @ClassName ResponseResultBodyAdvice
 * @Description 对响应进行拦截处理
 * @Date 2021/3/31 14:29
 * @Version 1.0
 */
@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {

    private static final Logger log = LoggerFactory.getLogger(ResponseResultBodyAdvice.class);

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

    /**
     * 判断类或者方法是否使用了 @ResponseResultBody
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 如果接口返回的类型本身就是Result那就没有必要进行额外的操作,返回false
        if (returnType.getGenericParameterType().equals(Result.class)) {
            return false;
        }
        return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ANNOTATION_TYPE) ||
                returnType.hasMethodAnnotation(ANNOTATION_TYPE);
    }

    /**
     * 当类或者方法使用了 @ResponseResultBody 就会调用这个方法
     */
    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        // 防止重复包裹的问题出现
        if (body instanceof Result) {
            return body;
        }
        // String类型不能直接包装,所以要进行些特别的处理
        if (returnType.getGenericParameterType().equals(String.class)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                // 将数据包装在Result里后,再转换为json字符串响应给前端
                return objectMapper.writeValueAsString(Result.success(body));
            } catch (JsonProcessingException e) {
                throw new BusinessException("返回String类型错误");
            }
        }
        Result<Object> success = Result.success(body);
        return success;
    }
    /**
     * 提供对标准Spring MVC异常的处理
     *
     * @param ex      the target exception
     * @param request the current request
     */
    @ExceptionHandler(Exception.class)
    public final Result exceptionHandler(Exception ex, WebRequest request) {
        log.error("ExceptionHandler: {}", ex.getMessage());
        HttpHeaders headers = new HttpHeaders();
        if (ex instanceof BusinessException) {
            return Result.fail(ex.getMessage());
        }
        return Result.fail(ResultStatus.INTERNAL_SERVER_ERROR, ex.getMessage());
    }

}

 

自定义异常:

/**
 * @Author
 * @ClassName ResultException
 * @Description 自定义异常处理类
 * @Date 2021/3/31 14:45
 * @Version 1.0
 */
public class BusinessException extends RuntimeException {

    private IResultStatus resultStatus;

    public BusinessException() {}
    public BusinessException(String message) {
        super(message);
    }
    public BusinessException(String message, IResultStatus resultStatus) {
        super(message);
        this.resultStatus = resultStatus;
    }

    public IResultStatus getResultStatus() {
        return resultStatus;
    }

    public void setResultStatus(IResultStatus resultStatus) {
        this.resultStatus = resultStatus;
    }
}

 

 

 

 

posted @ 2020-11-11 12:02  邓维-java  阅读(655)  评论(0)    收藏  举报