package com.backendsys.exception; import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DateUtil; import com.backendsys.modules.dingtalk.utils.DingTalkUtil; import com.fasterxml.jackson.databind.ObjectMapper; import com.backendsys.utils.response.Result; import com.backendsys.utils.response.ResultEnum; import com.google.common.collect.Lists; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.exceptions.TooManyResultsException; import org.mybatis.spring.MyBatisSystemException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.dao.DuplicateKeyException; import org.springframework.data.redis.RedisConnectionFailureException; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.security.access.AccessDeniedException; import org.springframework.validation.BindingResult; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import javax.security.auth.login.LoginException; import java.util.*; import java.util.concurrent.CompletableFuture; /** * https://juejin.cn/post/6844903902811275278 * https://juejin.cn/post/7132747726965440520 * 待看 */ @Slf4j @RestControllerAdvice public class GlobalExceptionHandler implements ResponseBodyAdvice { @Autowired private ObjectMapper objectMapper; /** * 是否开启功能 true:开启 */ @Override public boolean supports(MethodParameter methodParameter, Class> aClass) { return true; } /** * 返回结果拦截器 (可以修改响应体的逻辑,或添加其他自定义逻辑) */ @Override public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { // System.out.println("beforeBodyWrite:"); // System.out.println(o); // if(o instanceof String){ // return o; // try { // return objectMapper.writeValueAsString(Result.success(o)); // } catch (JsonProcessingException e) { // e.printStackTrace(); // } // } // //返回类型是否已经封装 // if(o instanceof Result){ // return Result.success(o); // } return o; } // -- 记录当前 访问URL、访问人IP -------------------------------------------------- private static void printRequestInfo() { // 获取请求的URL ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes != null) { HttpServletRequest request = attributes.getRequest(); if (request != null) { String method = request.getMethod(); String url = request.getRequestURL().toString(); String ip = request.getRemoteAddr(); // 记录URL和IP地址 log.warn("[" + method + "] " + url + ", (" + ip + ")"); } } } // -- 自定义异常输出结构 (警告) ---------------------------------------------------- private static void printWarnException(Exception e) { printRequestInfo(); log.warn(e.getClass().getName() + ": " + e.getMessage()); } // -- 自定义异常输出结构 (错误) ---------------------------------------------------- private static void printErrorException(Exception e, Boolean isPrintStack) { log.error("========================================================================"); // printRequestInfo(); // 获取请求的URL ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes != null) { HttpServletRequest request = attributes.getRequest(); if (request != null) { String url = request.getRequestURL().toString(); String ip = request.getRemoteAddr(); String method = request.getMethod(); String createTime = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"); String serverName = request.getServerName(); // 访问根目录报404也会触发此错误提示 // // --- 向钉钉发送消息 ----------------------------------- // // (本地环境不触发) // if (!"localhost".equals(serverName)) { // // 创建一个 CompletableFuture 来执行异步任务 // CompletableFuture future = CompletableFuture.runAsync(() -> { // try { // String message = "【系统异常】:" + e.getMessage() + "\n" + // "【异常接口】:" + url + "\n" + // "【异常堆栈】:" + Convert.toStr(e.getStackTrace()).substring(0, 400) + "\n" + // "【异常时间】:" + createTime + "\n" + // "【异常来源】:" + ip; // DingTalkUtil.sendTextMsg(message, Lists.newArrayList(), Lists.newLinkedList(), false); // } catch (Exception ex) { // System.out.println("钉钉群消息发送异常,异常原因:" + ex.getMessage()); // log.error(ex.getMessage()); // } // }); // } // // --------------------------------------------------- // 记录URL和IP地址 log.error("[" + method + "] " + url + ", (" + ip + ")"); } } // 记录 当前异常类的class名称、异常消息 log.error(e.getMessage() + " (" + e.getClass().getName() + ")"); // 记录异常的详细信息 if (isPrintStack) { log.error(Convert.toStr(e.getStackTrace())); e.printStackTrace(); } log.error("========================================================================"); } private static void printErrorException(Exception e) { printErrorException(e, false); } // ----------------------------------------------------------------------- /** * 异常 参数错误 */ @ExceptionHandler(ConstraintViolationException.class) public Result handleConstraintViolationException(ConstraintViolationException e) { System.out.println("****** ConstraintViolationException.class: ******"); printErrorException(e); Set> constraintViolations = e.getConstraintViolations(); Iterator> iterator = constraintViolations.iterator(); List msgList = new ArrayList<>(); while (iterator.hasNext()) { ConstraintViolation cvl = iterator.next(); msgList.add(cvl.getMessageTemplate()); } System.out.println(msgList.get(0)); log.error(msgList.get(0)); return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), msgList.get(0)); } /** * 参数校验异常 (Validate) */ // 未测试 @ExceptionHandler(MethodArgumentNotValidException.class) public Result handlerMethodArgumentException(MethodArgumentNotValidException e){ System.out.println("****** MethodArgumentNotValidException.class: ******"); printWarnException(e); return Result.error( ResultEnum.PARAMETER_EXCEPTION.getCode(), e.getBindingResult().getAllErrors().get(0).getDefaultMessage(), "Field: " + e.getBindingResult().getFieldError().getField() ); } @ExceptionHandler(MissingServletRequestParameterException.class) public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException e) { System.out.println("****** MissingServletRequestParameterException.class: ******"); printErrorException(e, true); return Result.error( ResultEnum.PARAMETER_EXCEPTION.getCode(), "缺少参数 " + e.getParameterName() + " 或类型不匹配 (" + e.getParameterType() + ")", e.getMessage() ); } /** * 参数类型异常 */ @ExceptionHandler(NumberFormatException.class) public Result handleNumberFormatException(NumberFormatException e) { System.out.println("****** NumberFormatException.class: ******"); printErrorException(e, true); return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), e.getMessage()); } /** * 自定义异常类 * throw new CustException("错误提示") */ @ExceptionHandler(CustException.class) public Result handleCustException(CustException e) { System.out.println("****** CustException.class: ******"); printWarnException(e); // printErrorException(e); return Result.error(e.getErrorCode() != null ? e.getErrorCode() : ResultEnum.PARAMETER_EXCEPTION.getCode(), e.getMessage(), e.getErrorObject()); } /** * 自定义异常类 (仅返回文本) */ @ExceptionHandler(CustExceptionSimple.class) public String handleCustExceptionSimple(CustExceptionSimple e) { System.out.println("****** CustExceptionSimple.class: ******"); printErrorException(e); return e.getMessage(); } /** * MyBatis异常 */ @ExceptionHandler(TooManyResultsException.class) public Result handleTooManyResultsException(TooManyResultsException e) { System.out.println("****** TooManyResultsException.class: ******"); printErrorException(e, true); return Result.error(ResultEnum.SERVICE_EXCEPTION.getCode(), e.getMessage()); } /** * 登录异常 */ @ExceptionHandler(LoginException.class) public Result handleLoginException(LoginException e) { System.out.println("****** LoginException.class: ******"); printErrorException(e, true); return Result.error(ResultEnum.AUTH_ERROR.getCode(), e.getMessage()); } /** * 权限异常 */ @ExceptionHandler(AccessDeniedException.class) public Result handleAccessDeniedException(AccessDeniedException e) { System.out.println("****** AccessDeniedException.class: ******"); printWarnException(e); return Result.error(ResultEnum.AUTH_ROLE_ERROR.getCode(), ResultEnum.AUTH_ROLE_ERROR.getMessage()); } /** * 接口不存在 异常 */ @ExceptionHandler(NoHandlerFoundException.class) public Result handleNoHandlerFoundException(NoHandlerFoundException e) { System.out.println("****** NoHandlerFoundException.class: ******"); printErrorException(e, true); return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), e.getMessage()); } /** * Mybatis 异常抛出 */ @ExceptionHandler(MyBatisSystemException.class) public Result handleMyBatisSystemException(MyBatisSystemException e, BindingResult bindingResult) { // , BindingResult bindingResult System.out.println("****** MyBatisSystemException.class: ******"); printErrorException(e, true); Throwable cause = e.getCause(); if (cause instanceof PersistenceException) { Throwable sqlException = cause.getCause(); if (sqlException != null ) { if (sqlException.getMessage() == "argument type mismatch") { System.out.println(sqlException.getMessage() + ", 参数类型不匹配"); log.error(sqlException.getMessage() + ", 参数类型不匹配"); } return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), ResultEnum.INTERNAL_ERROR.getMessage(), sqlException.getMessage()); } else { // [抛出异常] 实体类缺少字段 return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), ResultEnum.INTERNAL_ERROR.getMessage(), cause.getMessage()); } } return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), e.getMessage()); } /** * MySQL 数据库 唯一值异常 */ @ExceptionHandler(DuplicateKeyException.class) public Result handleDuplicateKeyException(DuplicateKeyException e) { System.out.println("****** DuplicateKeyException.class: ******"); printErrorException(e, true); DuplicateKeyExceptionHandler handler = new DuplicateKeyExceptionHandler(); return handler.handleDuplicateKeyException(e); } /** * Redis 连接异常 */ @ExceptionHandler(RedisConnectionFailureException.class) public Result handleRedisConnectionFailureException(Exception e) { System.out.println("****** RedisConnectionFailureException.class: ******"); printErrorException(e, true); return Result.error(ResultEnum.REDIS_ERROR.getCode(), e.getMessage()); } /** * 请求体参数不能为空 (body) */ @ExceptionHandler(HttpMessageNotReadableException.class) public Result handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { System.out.println("****** HttpMessageNotReadableException.class: ******"); printWarnException(e); return Result.error(ResultEnum.HTTP_BODY_EMPTY.getCode(), e.getMessage() != null ? e.getMessage() : ResultEnum.HTTP_BODY_EMPTY.getMessage()); } /** * 请求方法不支持 (Get/Post) */ @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { System.out.println("****** HttpRequestMethodNotSupportedException.class: ******"); printWarnException(e); return Result.error(ResultEnum.HTTP_METHOD_ERROR.getCode(), ResultEnum.HTTP_METHOD_ERROR.getMessage()); } /** * 其他异常: */ @ExceptionHandler(Exception.class) public Result handleException(Exception e) { // , BindingResult bindingResult System.out.println("****** Exception.class: ******"); printErrorException(e, true); return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), e.getMessage()); } // 非法参数 @ExceptionHandler(IllegalArgumentException.class) public Result handleIllegalArgumentException(IllegalArgumentException e) { System.out.println("****** IllegalArgumentException.class: ******"); printErrorException(e); return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), ResultEnum.PARAMETER_EXCEPTION.getMessage(), e.getMessage()); } // 优先级太高,会覆盖其他异常 // @ExceptionHandler(RuntimeException.class) // public Result handleRuntimeException(RuntimeException e) { // System.out.println("****** RuntimeException.class: ******"); // System.out.println(e); //// System.out.println(e.getMessage()); //// if (e.getClass().getTypeName() == "java.lang.NullPointerException") { //// return Result.error(ResultEnum.NULL_POINTER.getCode(), e.getMessage()); //// } // return Result.error(ResultEnum.SERVICE_EXCEPTION.getCode(), e.getMessage()); // } }