ServiceExceptionHandler.java 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /*
  2. * 爱组搭,低代码组件化开发平台
  3. * ------------------------------------------
  4. * 受知识产权保护,请勿删除版权申明,开发平台不允许做非法网站,后果自负
  5. */
  6. package com.aizuda.service.web;
  7. import com.aizuda.common.toolkit.ThrowableUtils;
  8. import com.aizuda.core.api.ApiResult;
  9. import com.aizuda.core.api.IErrorCode;
  10. import com.aizuda.core.exception.ApiException;
  11. import lombok.extern.slf4j.Slf4j;
  12. import org.springframework.beans.factory.annotation.Value;
  13. import org.springframework.http.HttpStatus;
  14. import org.springframework.http.converter.HttpMessageNotReadableException;
  15. import org.springframework.validation.BindException;
  16. import org.springframework.validation.FieldError;
  17. import org.springframework.web.bind.MethodArgumentNotValidException;
  18. import org.springframework.web.bind.annotation.ExceptionHandler;
  19. import org.springframework.web.bind.annotation.ResponseBody;
  20. import org.springframework.web.bind.annotation.ResponseStatus;
  21. import org.springframework.web.bind.annotation.RestControllerAdvice;
  22. import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
  23. import org.springframework.web.util.NestedServletException;
  24. import jakarta.servlet.http.HttpServletRequest;
  25. import jakarta.servlet.http.HttpServletResponse;
  26. import jakarta.validation.ConstraintViolationException;
  27. import java.util.List;
  28. import java.util.Optional;
  29. import java.util.stream.Collectors;
  30. import java.util.stream.Stream;
  31. /**
  32. * Service 异常处理
  33. *
  34. * @author 青苗
  35. * @since 2021-09-29
  36. */
  37. @Slf4j
  38. @RestControllerAdvice
  39. public class ServiceExceptionHandler {
  40. /**
  41. * 是否在响应结果中展示验证错误提示信息
  42. */
  43. @Value("${spring.validation.message.enable:true}")
  44. private Boolean enableValidationMessage;
  45. /**
  46. * 验证异常处理 - 在 @RequestBody 上添加 @Validated 处触发
  47. *
  48. * @param request {@link HttpServletRequest}
  49. * @param e {@link MethodArgumentNotValidException}
  50. * @return
  51. */
  52. @ExceptionHandler({MethodArgumentNotValidException.class})
  53. @ResponseStatus(HttpStatus.OK)
  54. @ResponseBody
  55. public ApiResult handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
  56. return ApiResult.failed(this.convertFiledErrors(e.getBindingResult().getFieldErrors()));
  57. }
  58. /**
  59. * 验证异常处理 - form参数(对象参数,没有加 @RequestBody)触发
  60. *
  61. * @param request {@link HttpServletRequest}
  62. * @param e {@link BindException}
  63. * @return
  64. */
  65. @ExceptionHandler({BindException.class})
  66. @ResponseStatus(HttpStatus.OK)
  67. @ResponseBody
  68. public ApiResult handleBindException(HttpServletRequest request, BindException e) {
  69. return ApiResult.failed(this.convertFiledErrors(e.getBindingResult().getFieldErrors()));
  70. }
  71. /**
  72. * 验证异常处理 - @Validated加在 controller 类上,
  73. * 且在参数列表中直接指定constraints时触发
  74. *
  75. * @param request
  76. * @param ex
  77. * @return
  78. */
  79. @ExceptionHandler({ConstraintViolationException.class})
  80. @ResponseStatus(HttpStatus.OK)
  81. @ResponseBody
  82. public ApiResult handleConstraintViolationException(HttpServletRequest request, ConstraintViolationException ex) {
  83. return ApiResult.failed(this.convertConstraintViolations(ex));
  84. }
  85. /**
  86. * 转换FieldError列表为错误提示信息
  87. *
  88. * @param fieldErrors
  89. * @return
  90. */
  91. private String convertFiledErrors(List<FieldError> fieldErrors) {
  92. return Optional.ofNullable(fieldErrors)
  93. .filter(fieldErrorsInner -> this.enableValidationMessage)
  94. .map(fieldErrorsInner -> fieldErrorsInner.stream()
  95. .flatMap(fieldError -> Stream.of(fieldError.getField() + " " + fieldError.getDefaultMessage()))
  96. .collect(Collectors.joining(", ")))
  97. .orElse(null);
  98. }
  99. /**
  100. * 转换ConstraintViolationException 异常为错误提示信息
  101. *
  102. * @param constraintViolationException
  103. * @return
  104. */
  105. private String convertConstraintViolations(ConstraintViolationException constraintViolationException) {
  106. return Optional.ofNullable(constraintViolationException.getConstraintViolations())
  107. .filter(constraintViolations -> this.enableValidationMessage)
  108. .map(constraintViolations -> constraintViolations.stream().flatMap(constraintViolation -> {
  109. String path = constraintViolation.getPropertyPath().toString();
  110. StringBuffer errorMessage = new StringBuffer();
  111. errorMessage.append(path.substring(path.lastIndexOf(".") + 1));
  112. errorMessage.append(" ").append(constraintViolation.getMessage());
  113. return Stream.of(errorMessage.toString());
  114. }).collect(Collectors.joining(", "))
  115. ).orElse(null);
  116. }
  117. /**
  118. * 自定义 REST 业务异常
  119. *
  120. * @param e 异常类型
  121. * @param resp 响应请求
  122. * @return
  123. */
  124. @ExceptionHandler(value = Throwable.class)
  125. public ApiResult<Object> handleBadRequest(Throwable e, HttpServletResponse resp) {
  126. /*
  127. * 业务逻辑异常
  128. */
  129. if (e instanceof ApiException) {
  130. IErrorCode errorCode = ((ApiException) e).getErrorCode();
  131. if (null != errorCode) {
  132. return ApiResult.failed(errorCode);
  133. }
  134. return ApiResult.failed(e.getMessage());
  135. }
  136. // 参数缺失
  137. if (e instanceof NestedServletException) {
  138. return ApiResult.failed(e.getMessage());
  139. }
  140. // 请求参数无法读取
  141. if (e instanceof HttpMessageNotReadableException) {
  142. return ApiResult.failed(e.getMessage());
  143. }
  144. /**
  145. * 系统内部异常,打印异常栈
  146. */
  147. log.error("Error: handleBadRequest StackTrace : {}", ThrowableUtils.getStackTrace(e));
  148. if (e instanceof MethodArgumentTypeMismatchException) {
  149. resp.setStatus(301);
  150. return ApiResult.failed("请求参数错误");
  151. }
  152. return ApiResult.failed("Internal Server Error");
  153. }
  154. }