springBoot统一响应类型3.4版本
前言:
通过实践而发现真理,又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识,又从理性认识而能动地指导革命实践,改造主观世界和客观世界。实践、认识、再实践、再认识,这种形式,循环往复以至无穷,而实践和认识之每一循环的内容,都比较地进到了高一级的程度。
简单回顾统一响应
统一响应的起源环境:在前后端分离的大背景下,为了提高前后端交流的效率和质量的一个工具
本系列内容:
1.0 抽象出统一响应的三个属性,利用构造函数
2.0 创建工具类,更优雅的创建统一响应对象
3.0 全局响应
3.3 全局异常响应
正片:
统一响应类
/** * 基础统一响应类 * @param <T> */ @Data public class apiResult<T> { private int code; private String message; private T data; /** * 带有data返回的构造函数 * @param code * @param message * @param data */ public apiResult(int code, String message, T data) { this.code = code; this.message = message; this.data = data; } /** * 不带data的构造函数 * @param code * @param message */ public apiResult(int code,String message){ this.code = code; this.message = message; } }
该版本足够适用百分之98以上的情况了!
剩下百分之2呢?
全局响应
全局异常响应
为了更好的实现全局响应和全局异常响应,创建一个工具类,专门用于构建成功和失败响应的实例
统一响应工具类
/** * 统一响应对象构建工具类 * <p>提供快速创建标准化API响应结果的工厂方法,封装常见成功/错误响应构造逻辑 * * @see org.springframework.http.HttpStatus HTTP状态码常量参考 */ public class apiResultUnit { /** * 默认成功HTTP状态码 (200 OK) */ private static final int OK = HttpStatus.OK.value(); /** * 默认错误HTTP状态码 (500 Internal Server Error) */ private static int NO = HttpStatus.INTERNAL_SERVER_ERROR.value(); /** * 基本响应 * * @param data 响应数据 * @param <T> 响应数据类型 * @return */ public static <T> apiResult<T> success(T data) { return new apiResult<>(OK, "操作成功", data); } /** * 自定义信息统一响应 * @param message 自定义提示信息 * @param data 响应数据 * @return * @param <T> 响应数据类型 */ public static <T> apiResult<T> success(String message, T data) { return new apiResult<>(OK, message, data); } /** * 完全自定义统一响应 * @param code 自定义提示编码 * @param message 自定义提示信息 * @param data 响应数据 * @return * @param <T> 响应数据类型 */ public static <T> apiResult<T> success(int code, String message, T data) { return new apiResult<>(code, message, data); } /** * 基本失败响应 * @return * @param <T> 响应数据类型 */ public static <T> apiResult<T> error() { return new apiResult<>(NO, "系统错误,请联系管理员"); } /** * 自定义信息统一失败响应 * @param message 自定义提示信息 * @return * @param <T> 响应数据类型 */ public static <T> apiResult<T> error(String message) { return new apiResult<>(NO, message); } /** * 完全自定义统一失败响应 * @param code 自定义编码 * @param message 自定义提示信息 * @return * @param <T> 响应数据类型 */ public static <T> apiResult<T> error(int code, String message) { return new apiResult<>(code, message); } }
在新版本中,更换了全局响应和全局异常响应的顺序,所以先完成全局异常响应
新创建一个全局异常处理类
为什么要新建一个呢?目前真不知道,因为别人就是这么写的,写了还成了,等后续实践加深
业务异常响应类
@Data @AllArgsConstructor public class BusinessException extends RuntimeException { /** * 异常编码 * 异常信息 */ private int code; private String message; /** * 无参构造函数 */ public BusinessException() { super(); } }
代码简简单单
核心点在于继承了RuntimeException,从名字来看很简单理解,运行时的异常
public class RuntimeException extends Exception {
@java.io.Serial
static final long serialVersionUID = -7034897190745766939L;
/** Constructs a new runtime exception with {@code null} as its
* detail message. The cause is not initialized, and may subsequently be
* initialized by a call to {@link #initCause}.
*/
public RuntimeException() {
super();
}
/** Constructs a new runtime exception with the specified detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public RuntimeException(String message) {
super(message);
}
/**
* Constructs a new runtime exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this runtime exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A {@code null} value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public RuntimeException(String message, Throwable cause) {
super(message, cause);
}
/** Constructs a new runtime exception with the specified cause and a
* detail message of {@code (cause==null ? null : cause.toString())}
* (which typically contains the class and detail message of
* {@code cause}). This constructor is useful for runtime exceptions
* that are little more than wrappers for other throwables.
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A {@code null} value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public RuntimeException(Throwable cause) {
super(cause);
}
/**
* Constructs a new runtime exception with the specified detail
* message, cause, suppression enabled or disabled, and writable
* stack trace enabled or disabled.
*
* @param message the detail message.
* @param cause the cause. (A {@code null} value is permitted,
* and indicates that the cause is nonexistent or unknown.)
* @param enableSuppression whether or not suppression is enabled
* or disabled
* @param writableStackTrace whether or not the stack trace should
* be writable
*
* @since 1.7
*/
protected RuntimeException(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
尽管作者看不懂英文,但是根据代码的内容,可以知道,继承这个类是为了读取Throwable里的
message
RuntimeException里是没有各种信息的,它的信息来源于父类的Throwable,继承该类是为了将业务异常和系统异常区分开
全局异常响应配置(简易版)
@Slf4j @RestControllerAdvice public class GlobalExcResult { @ExceptionHandler(Throwable.class) public apiResult handleException(Throwable throwable){ if (throwable instanceof BusinessException){ BusinessException e = new BusinessException(); log.info("请求出现业务异常:",throwable); return apiResultUnit.error(e.getCode(),e.getMessage()); } log.error("请求出现系统异常:",throwable); return apiResultUnit.error("服务器内部错误"); } }
目前阶段,这个业务判断无用
为什么呢?
因为我们还没有写BusinessException里的内容
所以现在的报错,基本都是运行时的报错
看看效果
@GetMapping("/id")
public apiResult<UserPageEntity> id(int id) {
UserPageEntity userPageEntity = userPageServer.UserByID(id);
return apiResultUnit.success(userPageEntity);
}
测试代码,简单的一个返回
如果报错,它会返回一个内部服务器错误
半全局响应配置
@Slf4j @ControllerAdvice public class GlobalApiResult implements ResponseBodyAdvice<Object> { /** * 此Advice是否使用于该返回类型和Converter类型(意思是可以配置多个哦) * @param returnType 接口类型详细:包括路径,参数等 * @param converterType 自动选择的转换器类型 * @return 返回true表示将会走接下来的方法(beforeBodyWrite), 否则不会 */ @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { //false -> true 用于测试,正式的时需要修改 return true; } /** * HttpMessageConverter转换之前进行的操作 * @param body 业务层接口返回值 * @param returnType 返回类型 * @param selectedContentType 根据请求头协商的ContentType * @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) { //第二步: 将body传入统一响应工具类中的默认成功响应 return apiResultUnit.success("3.0全新版本",body); } }
运行
响应内容来自body,给上断点看看
我们将body转换一下类型即可
真(全局响应配置)
@Slf4j
@ControllerAdvice
public class GlobalApiResult implements ResponseBodyAdvice<Object> {
/**
* 此Advice是否使用于该返回类型和Converter类型(意思是可以配置多个哦)
* @param returnType 接口类型详细:包括路径,参数等
* @param converterType 自动选择的转换器类型
* @return 返回true表示将会走接下来的方法(beforeBodyWrite), 否则不会
*/
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
//false -> true 用于测试,正式的时需要修改
return true;
}
/**
* HttpMessageConverter转换之前进行的操作
* @param body 业务层接口返回值
* @param returnType 返回类型
* @param selectedContentType 根据请求头协商的ContentType
* @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) {
//类型转换
if(body instanceof apiResult){
return (apiResult)body;
}
//第二步: 将body传入统一响应工具类中的默认成功响应
return apiResultUnit.success("3.0全新版本",body);
}
}
最垃圾最简易的全局响应如上