diff --git a/yami-shop-common/src/main/java/com/yami/shop/common/config/DefaultExceptionHandlerConfig.java b/yami-shop-common/src/main/java/com/yami/shop/common/config/DefaultExceptionHandlerConfig.java new file mode 100644 index 0000000..3dd3af1 --- /dev/null +++ b/yami-shop-common/src/main/java/com/yami/shop/common/config/DefaultExceptionHandlerConfig.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved. + * + * https://www.mall4j.com/ + * + * 未经允许,不可做商业用途! + * + * 版权所有,侵权必究! + */ + +package com.yami.shop.common.config; + +// 导入相关的自定义异常类、响应相关的枚举、响应实体类等 +import com.yami.shop.common.exception.YamiShopBindException; +import com.yami.shop.common.response.ResponseEnum; +import com.yami.shop.common.response.ServerResponseEntity; +// 导入日志相关的Lombok注解,用于简化日志记录代码 +import lombok.extern.slf4j.Slf4j; +// 导入Spring的HTTP状态码相关枚举类 +import org.springframework.http.HttpStatus; +// 导入Spring用于构建HTTP响应实体的类 +import org.springframework.http.ResponseEntity; +// 导入Spring用于标记控制器类的注解(此处结合异常处理相关特性使用) +import org.springframework.stereotype.Controller; +// 导入Spring在数据校验场景下的异常类以及表示字段错误的类 +import org.springframework.validation.BindException; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +// 导入Spring用于定义异常处理方法的注解以及标记全局异常处理类的注解 +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +// 导入Spring用于处理资源未找到异常的类 +import org.springframework.web.servlet.resource.NoResourceFoundException; + +import java.util.ArrayList; +import java.util.List; + +/** + * 自定义错误处理器,用于统一处理项目中的各类异常情况, + * 并将异常信息按照一定格式封装到响应实体中返回给客户端。 + * @author LGH + */ +@Slf4j +@Controller +@RestControllerAdvice +public class DefaultExceptionHandlerConfig { + + /** + * 处理MethodArgumentNotValidException和BindException这两种参数校验相关异常的方法。 + * 当在Spring的参数校验过程中出现这两种异常时,会进入此方法进行处理, + * 将异常中的字段错误信息提取出来,封装到响应实体中返回给客户端。 + * + * @param e 捕获到的异常对象,可能是MethodArgumentNotValidException或者BindException类型 + * @return ResponseEntity>> 包含了处理后的响应实体的HTTP响应实体, + * 响应实体中包含了参数校验失败的具体字段错误信息列表(如果有)以及对应的响应状态等信息。 + */ + @ExceptionHandler({ MethodArgumentNotValidException.class, BindException.class }) + public ResponseEntity>> methodArgumentNotValidExceptionHandler(Exception e) { + // 记录异常信息,方便后续查看出现异常的具体情况,用于调试和问题排查 + log.error("methodArgumentNotValidExceptionHandler", e); + List fieldErrors = null; + // 判断异常类型是否是MethodArgumentNotValidException,如果是则获取对应的字段错误列表 + if (e instanceof MethodArgumentNotValidException) { + fieldErrors = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors(); + } + // 判断异常类型是否是BindException,如果是则获取对应的字段错误列表 + if (e instanceof BindException) { + fieldErrors = ((BindException) e).getBindingResult().getFieldErrors(); + } + // 如果没有获取到字段错误列表(可能出现不符合预期的异常情况等) + if (fieldErrors == null) { + // 返回一个包含特定失败响应枚举(表示参数校验不通过)的响应实体,没有具体字段错误信息 + return ResponseEntity.status(HttpStatus.OK) + .body(ServerResponseEntity.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID)); + } + + // 用于存放每个字段的错误信息字符串(格式为:字段名:默认错误消息) + List defaultMessages = new ArrayList<>(fieldErrors.size()); + // 遍历字段错误列表,拼接每个字段的错误信息字符串并添加到defaultMessages列表中 + for (FieldError fieldError : fieldErrors) { + defaultMessages.add(fieldError.getField() + ":" + fieldError.getDefaultMessage()); + } + // 返回一个包含具体字段错误信息列表的失败响应实体,使用参数校验不通过的响应枚举标识 + return ResponseEntity.status(HttpStatus.OK) + .body(ServerResponseEntity.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID, defaultMessages)); + } + + /** + * 处理YamiShopBindException这种自定义异常的方法。 + * 当项目中抛出YamiShopBindException异常时,会进入此方法进行处理, + * 根据异常中封装的响应实体(如果有)或者异常本身的错误码和错误消息构建响应实体返回给客户端。 + * + * @param e 捕获到的YamiShopBindException异常对象 + * @return ResponseEntity> 包含了处理后的响应实体的HTTP响应实体, + * 响应实体中包含了对应业务逻辑异常的相关信息以及响应状态等。 + */ + @ExceptionHandler(YamiShopBindException.class) + public ResponseEntity> unauthorizedExceptionHandler(YamiShopBindException e){ + // 记录异常信息,方便后续排查问题,了解异常出现的具体情况 + log.error("mall4jExceptionHandler", e); + + ServerResponseEntity serverResponseEntity = e.getServerResponseEntity(); + // 如果异常中已经封装好了响应实体(在抛出异常时可能已经构建好了特定的响应内容) + if (serverResponseEntity!=null) { + // 直接将其作为响应内容返回给客户端,设置状态码为OK + return ResponseEntity.status(HttpStatus.OK).body(serverResponseEntity); + } + // 失败返回消息,状态码固定为直接显示消息的状态码,根据异常的错误码和错误消息构建失败响应实体并返回 + return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.fail(e.getCode(),e.getMessage())); + } + + /** + * 兜底的异常处理方法,处理所有未被其他异常处理方法处理的Exception类型异常。 + * 对于不同类型的通用异常进行相应处理,如资源未找到异常返回特定消息的响应实体, + * 其他异常则返回默认的异常响应实体告知客户端出现了未预期的异常情况。 + * + * @param e 捕获到的异常对象,是Exception类型 + * @return ResponseEntity> 包含了处理后的响应实体的HTTP响应实体, + * 响应实体中包含了对应异常的相关信息以及响应状态等。 + */ + @ExceptionHandler(Exception.class) + public ResponseEntity> exceptionHandler(Exception e){ + // 判断异常是否是资源未找到异常类型 + if (e instanceof NoResourceFoundException) { + // 如果是,则返回一个包含异常消息的失败响应实体,告知客户端资源未找到相关错误信息,状态码设为OK + return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.showFailMsg(e.getMessage())); + } + // 记录异常信息,方便后续排查问题,知道出现异常的具体情况 + log.error("exceptionHandler", e); + // 返回一个使用默认的异常响应枚举构建的失败响应实体,告知客户端出现了未预期的通用异常情况 + return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.fail(ResponseEnum.EXCEPTION)); + } +} \ No newline at end of file