( 二十六 )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;
}
}