GlobalExceptionHandler.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. package com.backendsys.exception;
  2. import cn.hutool.core.convert.Convert;
  3. import cn.hutool.core.date.DateUtil;
  4. import com.backendsys.modules.dingtalk.utils.DingTalkUtil;
  5. import com.backendsys.utils.response.ResultEnumService;
  6. import com.fasterxml.jackson.databind.ObjectMapper;
  7. import com.backendsys.utils.response.Result;
  8. import com.backendsys.utils.response.ResultEnum;
  9. import com.google.common.collect.Lists;
  10. import jakarta.servlet.http.HttpServletRequest;
  11. import jakarta.validation.ConstraintViolation;
  12. import jakarta.validation.ConstraintViolationException;
  13. import lombok.extern.slf4j.Slf4j;
  14. import org.apache.ibatis.binding.BindingException;
  15. import org.apache.ibatis.exceptions.PersistenceException;
  16. import org.apache.ibatis.exceptions.TooManyResultsException;
  17. import org.mybatis.spring.MyBatisSystemException;
  18. import org.springframework.beans.factory.annotation.Autowired;
  19. import org.springframework.core.MethodParameter;
  20. import org.springframework.dao.DuplicateKeyException;
  21. import org.springframework.data.redis.RedisConnectionFailureException;
  22. import org.springframework.http.MediaType;
  23. import org.springframework.http.converter.HttpMessageConverter;
  24. import org.springframework.http.converter.HttpMessageNotReadableException;
  25. import org.springframework.http.server.ServerHttpRequest;
  26. import org.springframework.http.server.ServerHttpResponse;
  27. import org.springframework.security.access.AccessDeniedException;
  28. import org.springframework.validation.BindingResult;
  29. import org.springframework.web.HttpRequestMethodNotSupportedException;
  30. import org.springframework.web.bind.MethodArgumentNotValidException;
  31. import org.springframework.web.bind.MissingServletRequestParameterException;
  32. import org.springframework.web.bind.annotation.ExceptionHandler;
  33. import org.springframework.web.bind.annotation.RestControllerAdvice;
  34. import org.springframework.web.context.request.RequestContextHolder;
  35. import org.springframework.web.context.request.ServletRequestAttributes;
  36. import org.springframework.web.servlet.NoHandlerFoundException;
  37. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
  38. import javax.security.auth.login.LoginException;
  39. import java.util.*;
  40. import java.util.concurrent.CompletableFuture;
  41. /**
  42. * https://juejin.cn/post/6844903902811275278
  43. * https://juejin.cn/post/7132747726965440520
  44. * 待看
  45. */
  46. @Slf4j
  47. @RestControllerAdvice
  48. public class GlobalExceptionHandler implements ResponseBodyAdvice<Object> {
  49. @Autowired
  50. private ObjectMapper objectMapper;
  51. @Autowired
  52. private ResultEnumService resultEnumService;
  53. /**
  54. * 是否开启功能 true:开启
  55. */
  56. @Override
  57. public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
  58. return true;
  59. }
  60. /**
  61. * 返回结果拦截器 (可以修改响应体的逻辑,或添加其他自定义逻辑)
  62. */
  63. @Override
  64. public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
  65. // System.out.println("beforeBodyWrite:");
  66. // System.out.println(o);
  67. // if(o instanceof String){
  68. // return o;
  69. // try {
  70. // return objectMapper.writeValueAsString(Result.success(o));
  71. // } catch (JsonProcessingException e) {
  72. // e.printStackTrace();
  73. // }
  74. // }
  75. // //返回类型是否已经封装
  76. // if(o instanceof Result){
  77. // return Result.success(o);
  78. // }
  79. return o;
  80. }
  81. // -- 记录当前 访问URL、访问人IP --------------------------------------------------
  82. private static void printRequestInfo() {
  83. // 获取请求的URL
  84. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  85. if (attributes != null) {
  86. HttpServletRequest request = attributes.getRequest();
  87. if (request != null) {
  88. String method = request.getMethod();
  89. String url = request.getRequestURL().toString();
  90. String ip = request.getRemoteAddr();
  91. // 记录URL和IP地址
  92. log.warn("[" + method + "] " + url + ", (" + ip + ")");
  93. }
  94. }
  95. }
  96. // -- 自定义异常输出结构 (警告) ----------------------------------------------------
  97. private static void printWarnException(Exception e) {
  98. printRequestInfo();
  99. log.warn(e.getClass().getName() + ": " + e.getMessage());
  100. }
  101. // -- 自定义异常输出结构 (错误) ----------------------------------------------------
  102. private static void printErrorException(Exception e, Boolean isPrintStack) {
  103. log.error("========================================================================");
  104. // printRequestInfo();
  105. // 获取请求的URL
  106. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  107. if (attributes != null) {
  108. HttpServletRequest request = attributes.getRequest();
  109. if (request != null) {
  110. String url = request.getRequestURL().toString();
  111. String ip = request.getRemoteAddr();
  112. String method = request.getMethod();
  113. String createTime = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
  114. String serverName = request.getServerName();
  115. // 访问根目录报404也会触发此错误提示
  116. // // --- 向钉钉发送消息 -----------------------------------
  117. // // (本地环境不触发)
  118. // if (!"localhost".equals(serverName)) {
  119. // // 创建一个 CompletableFuture 来执行异步任务
  120. // CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
  121. // try {
  122. // String message = "【系统异常】:" + e.getMessage() + "\n" +
  123. // "【异常接口】:" + url + "\n" +
  124. // "【异常堆栈】:" + Convert.toStr(e.getStackTrace()).substring(0, 400) + "\n" +
  125. // "【异常时间】:" + createTime + "\n" +
  126. // "【异常来源】:" + ip;
  127. // DingTalkUtil.sendTextMsg(message, Lists.newArrayList(), Lists.newLinkedList(), false);
  128. // } catch (Exception ex) {
  129. // System.out.println("钉钉群消息发送异常,异常原因:" + ex.getMessage());
  130. // log.error(ex.getMessage());
  131. // }
  132. // });
  133. // }
  134. // // ---------------------------------------------------
  135. // 记录URL和IP地址
  136. log.error("[" + method + "] " + url + ", (" + ip + ")");
  137. }
  138. }
  139. // 记录 当前异常类的class名称、异常消息
  140. log.error(e.getMessage() + " (" + e.getClass().getName() + ")");
  141. // 记录异常的详细信息
  142. if (isPrintStack) {
  143. // log.error(Convert.toStr(e.getStackTrace()).substring(0, 400));
  144. e.printStackTrace();
  145. }
  146. log.error("========================================================================");
  147. }
  148. private static void printErrorException(Exception e) {
  149. printErrorException(e, false);
  150. }
  151. // -----------------------------------------------------------------------
  152. /**
  153. * 异常 参数错误
  154. */
  155. @ExceptionHandler(ConstraintViolationException.class)
  156. public Result handleConstraintViolationException(ConstraintViolationException e) {
  157. System.out.println("****** ConstraintViolationException.class: ******");
  158. printErrorException(e);
  159. Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
  160. Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
  161. List<String> msgList = new ArrayList<>();
  162. while (iterator.hasNext()) {
  163. ConstraintViolation<?> cvl = iterator.next();
  164. msgList.add(cvl.getMessageTemplate());
  165. }
  166. System.out.println(msgList.get(0));
  167. log.error(msgList.get(0));
  168. return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), msgList.get(0));
  169. }
  170. /**
  171. * 参数校验异常 (Validate)
  172. */
  173. // 未测试
  174. @ExceptionHandler(MethodArgumentNotValidException.class)
  175. public Result handlerMethodArgumentException(MethodArgumentNotValidException e){
  176. System.out.println("****** MethodArgumentNotValidException.class: ******");
  177. printWarnException(e);
  178. return Result.error(
  179. ResultEnum.PARAMETER_EXCEPTION.getCode(),
  180. e.getBindingResult().getAllErrors().get(0).getDefaultMessage(),
  181. "Field: " + e.getBindingResult().getFieldError().getField()
  182. );
  183. }
  184. @ExceptionHandler(MissingServletRequestParameterException.class)
  185. public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
  186. System.out.println("****** MissingServletRequestParameterException.class: ******");
  187. printErrorException(e, false);
  188. return Result.error(
  189. ResultEnum.PARAMETER_EXCEPTION.getCode(),
  190. "缺少参数(" + e.getParameterName() + ")或参数类型不匹配(" + e.getParameterType() + ")",
  191. "Field: " + e.getParameterName()
  192. );
  193. }
  194. // 无效?
  195. @ExceptionHandler(IllegalStateException.class)
  196. public Result handleIllegalStateException(IllegalStateException e) {
  197. printErrorException(e, true);
  198. return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), e.getMessage());
  199. }
  200. @ExceptionHandler(BindingException.class)
  201. public Result handleBindingException(BindingException e) {
  202. printErrorException(e, true);
  203. return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), e.getMessage());
  204. }
  205. /**
  206. * 参数类型异常
  207. */
  208. @ExceptionHandler(NumberFormatException.class)
  209. public Result handleNumberFormatException(NumberFormatException e) {
  210. System.out.println("****** NumberFormatException.class: ******");
  211. printErrorException(e, true);
  212. return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), e.getMessage());
  213. }
  214. /**
  215. * 自定义异常类
  216. * throw new CustException("错误提示")
  217. */
  218. @ExceptionHandler(CustException.class)
  219. public Result handleCustException(CustException e) {
  220. System.out.println("****** CustException.class: ******");
  221. printWarnException(e);
  222. // printErrorException(e);
  223. return Result.error(e.getErrorCode() != null ? e.getErrorCode() : ResultEnum.PARAMETER_EXCEPTION.getCode(), e.getMessage(), e.getErrorObject());
  224. }
  225. /**
  226. * 自定义异常类 (仅返回文本)
  227. */
  228. @ExceptionHandler(CustExceptionSimple.class)
  229. public String handleCustExceptionSimple(CustExceptionSimple e) {
  230. System.out.println("****** CustExceptionSimple.class: ******");
  231. printErrorException(e);
  232. return e.getMessage();
  233. }
  234. /**
  235. * MyBatis异常
  236. */
  237. @ExceptionHandler(TooManyResultsException.class)
  238. public Result handleTooManyResultsException(TooManyResultsException e) {
  239. System.out.println("****** TooManyResultsException.class: ******");
  240. printErrorException(e, true);
  241. return Result.error(ResultEnum.SERVICE_EXCEPTION.getCode(), e.getMessage());
  242. }
  243. /**
  244. * 登录异常
  245. */
  246. @ExceptionHandler(LoginException.class)
  247. public Result handleLoginException(LoginException e) {
  248. System.out.println("****** LoginException.class: ******");
  249. printErrorException(e, true);
  250. return Result.error(ResultEnum.AUTH_ERROR.getCode(), e.getMessage());
  251. }
  252. /**
  253. * 权限异常
  254. */
  255. @ExceptionHandler(AccessDeniedException.class)
  256. public Result handleAccessDeniedException(AccessDeniedException e) {
  257. System.out.println("****** AccessDeniedException.class: ******");
  258. printWarnException(e);
  259. // return Result.error(ResultEnum.AUTH_ROLE_ERROR.getCode(), ResultEnum.AUTH_ROLE_ERROR.getMessage());
  260. return Result.error(ResultEnum.AUTH_ROLE_ERROR.getCode(), resultEnumService.getMessage(ResultEnum.AUTH_ROLE_ERROR));
  261. }
  262. /**
  263. * 接口不存在 异常
  264. */
  265. @ExceptionHandler(NoHandlerFoundException.class)
  266. public Result handleNoHandlerFoundException(NoHandlerFoundException e) {
  267. System.out.println("****** NoHandlerFoundException.class: ******");
  268. printErrorException(e, false);
  269. return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), e.getMessage());
  270. }
  271. /**
  272. * Mybatis 异常抛出
  273. */
  274. @ExceptionHandler(MyBatisSystemException.class)
  275. public Result handleMyBatisSystemException(MyBatisSystemException e, BindingResult bindingResult) { // , BindingResult bindingResult
  276. System.out.println("****** MyBatisSystemException.class: ******");
  277. printErrorException(e, true);
  278. Throwable cause = e.getCause();
  279. if (cause instanceof PersistenceException) {
  280. Throwable sqlException = cause.getCause();
  281. if (sqlException != null ) {
  282. if (sqlException.getMessage() == "argument type mismatch") {
  283. System.out.println(sqlException.getMessage() + ", 参数类型不匹配");
  284. log.error(sqlException.getMessage() + ", 参数类型不匹配");
  285. }
  286. // return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), ResultEnum.INTERNAL_ERROR.getMessage(), sqlException.getMessage());
  287. return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), resultEnumService.getMessage(ResultEnum.INTERNAL_ERROR), sqlException.getMessage());
  288. } else {
  289. // [抛出异常] 实体类缺少字段
  290. // return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), ResultEnum.INTERNAL_ERROR.getMessage(), cause.getMessage());
  291. return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), resultEnumService.getMessage(ResultEnum.INTERNAL_ERROR), cause.getMessage());
  292. }
  293. }
  294. return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), e.getMessage());
  295. }
  296. /**
  297. * MySQL 数据库 唯一值异常
  298. */
  299. @ExceptionHandler(DuplicateKeyException.class)
  300. public Result handleDuplicateKeyException(DuplicateKeyException e) {
  301. System.out.println("****** DuplicateKeyException.class: ******");
  302. printErrorException(e, true);
  303. DuplicateKeyExceptionHandler handler = new DuplicateKeyExceptionHandler();
  304. return handler.handleDuplicateKeyException(e);
  305. }
  306. /**
  307. * Redis 连接异常
  308. */
  309. @ExceptionHandler(RedisConnectionFailureException.class)
  310. public Result handleRedisConnectionFailureException(Exception e) {
  311. System.out.println("****** RedisConnectionFailureException.class: ******");
  312. printErrorException(e, true);
  313. return Result.error(ResultEnum.REDIS_ERROR.getCode(), e.getMessage());
  314. }
  315. /**
  316. * 请求体参数不能为空 (body)
  317. */
  318. @ExceptionHandler(HttpMessageNotReadableException.class)
  319. public Result handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
  320. System.out.println("****** HttpMessageNotReadableException.class: ******");
  321. printWarnException(e);
  322. return Result.error(ResultEnum.HTTP_BODY_EMPTY.getCode(), e.getMessage() != null ? e.getMessage() : ResultEnum.HTTP_BODY_EMPTY.getMessage());
  323. }
  324. /**
  325. * 请求方法不支持 (Get/Post)
  326. */
  327. @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
  328. public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
  329. System.out.println("****** HttpRequestMethodNotSupportedException.class: ******");
  330. printWarnException(e);
  331. // return Result.error(ResultEnum.HTTP_METHOD_ERROR.getCode(), ResultEnum.HTTP_METHOD_ERROR.getMessage());
  332. return Result.error(ResultEnum.HTTP_METHOD_ERROR.getCode(), resultEnumService.getMessage(ResultEnum.HTTP_METHOD_ERROR));
  333. }
  334. /**
  335. * 其他异常:
  336. */
  337. @ExceptionHandler(Exception.class)
  338. public Result handleException(Exception e) { // , BindingResult bindingResult
  339. System.out.println("****** Exception.class: ******");
  340. printErrorException(e, true);
  341. return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), e.getMessage());
  342. }
  343. // 非法参数
  344. @ExceptionHandler(IllegalArgumentException.class)
  345. public Result handleIllegalArgumentException(IllegalArgumentException e) {
  346. System.out.println("****** IllegalArgumentException.class: ******");
  347. printErrorException(e);
  348. // return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), ResultEnum.PARAMETER_EXCEPTION.getMessage(), e.getMessage());
  349. return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), resultEnumService.getMessage(ResultEnum.PARAMETER_EXCEPTION), e.getMessage());
  350. }
  351. // 优先级太高,会覆盖其他异常
  352. // @ExceptionHandler(RuntimeException.class)
  353. // public Result handleRuntimeException(RuntimeException e) {
  354. // System.out.println("****** RuntimeException.class: ******");
  355. // System.out.println(e);
  356. //// System.out.println(e.getMessage());
  357. //// if (e.getClass().getTypeName() == "java.lang.NullPointerException") {
  358. //// return Result.error(ResultEnum.NULL_POINTER.getCode(), e.getMessage());
  359. //// }
  360. // return Result.error(ResultEnum.SERVICE_EXCEPTION.getCode(), e.getMessage());
  361. // }
  362. }