From f3920a927cbc126f6307b0e0e92d4bd56b7d7c85 Mon Sep 17 00:00:00 2001 From: liaozan <378024053@qq.com> Date: Sun, 18 Jun 2023 00:20:52 +0800 Subject: [PATCH] Refine exception handing --- commons/web-common/pom.xml | 2 +- .../web/ExceptionHandingConfiguration.java | 12 +- .../DefaultGlobalExceptionHandler.java | 252 ------------------ .../DefaultGlobalExceptionResolver.java | 10 +- ... => ExceptionHandingWebMvcConfigurer.java} | 12 +- .../web/exception/GlobalExceptionHandler.java | 249 ++++++++++++++++- 6 files changed, 261 insertions(+), 276 deletions(-) delete mode 100644 commons/web-common/src/main/java/com/schbrain/common/web/exception/DefaultGlobalExceptionHandler.java rename commons/web-common/src/main/java/com/schbrain/common/web/exception/{ExceptionHandlerWebMcvConfigurer.java => ExceptionHandingWebMvcConfigurer.java} (75%) diff --git a/commons/web-common/pom.xml b/commons/web-common/pom.xml index 93f10eb..7118f08 100644 --- a/commons/web-common/pom.xml +++ b/commons/web-common/pom.xml @@ -31,7 +31,7 @@ org.springframework.boot spring-boot-configuration-processor - + org.springframework spring-tx diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/ExceptionHandingConfiguration.java b/commons/web-common/src/main/java/com/schbrain/common/web/ExceptionHandingConfiguration.java index 6be2a55..2333b66 100644 --- a/commons/web-common/src/main/java/com/schbrain/common/web/ExceptionHandingConfiguration.java +++ b/commons/web-common/src/main/java/com/schbrain/common/web/ExceptionHandingConfiguration.java @@ -18,23 +18,21 @@ import java.util.stream.Collectors; public class ExceptionHandingConfiguration { @Bean - @ConditionalOnMissingBean public GlobalExceptionHandler defaultGlobalExceptionHandler(ObjectProvider exceptionTranslators) { - return new DefaultGlobalExceptionHandler(exceptionTranslators.orderedStream().collect(Collectors.toList())); + return new GlobalExceptionHandler(exceptionTranslators.orderedStream().collect(Collectors.toList())); } @Bean @ConditionalOnMissingBean - @ConditionalOnBean(GlobalExceptionHandler.class) - public ExceptionTranslator defaultExceptionTranslator() { - return new DefaultExceptionTranslator(); + public ExceptionHandingWebMvcConfigurer defaultExceptionHandingWebMvcConfigurer(WebProperties webProperties, GlobalExceptionHandler exceptionHandler) { + return new ExceptionHandingWebMvcConfigurer(webProperties, exceptionHandler); } @Bean @ConditionalOnMissingBean @ConditionalOnBean(GlobalExceptionHandler.class) - public ExceptionHandlerWebMcvConfigurer defaultExceptionHandlerWebMcvConfigurer(WebProperties webProperties, GlobalExceptionHandler exceptionHandler) { - return new ExceptionHandlerWebMcvConfigurer(webProperties, exceptionHandler); + public ExceptionTranslator defaultExceptionTranslator() { + return new DefaultExceptionTranslator(); } } \ No newline at end of file diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/exception/DefaultGlobalExceptionHandler.java b/commons/web-common/src/main/java/com/schbrain/common/web/exception/DefaultGlobalExceptionHandler.java deleted file mode 100644 index dce828f..0000000 --- a/commons/web-common/src/main/java/com/schbrain/common/web/exception/DefaultGlobalExceptionHandler.java +++ /dev/null @@ -1,252 +0,0 @@ -package com.schbrain.common.web.exception; - -import cn.hutool.core.exceptions.ExceptionUtil; -import cn.hutool.core.util.StrUtil; -import com.schbrain.common.constants.ResponseActionConstants; -import com.schbrain.common.exception.BaseException; -import com.schbrain.common.web.result.ResponseDTO; -import lombok.extern.slf4j.Slf4j; -import org.hibernate.validator.internal.engine.path.PathImpl; -import org.springframework.dao.DataAccessException; -import org.springframework.http.HttpStatus; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.util.ClassUtils; -import org.springframework.validation.*; -import org.springframework.web.*; -import org.springframework.web.bind.*; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.context.request.async.AsyncRequestTimeoutException; -import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; -import org.springframework.web.multipart.support.MissingServletRequestPartException; -import org.springframework.web.servlet.NoHandlerFoundException; - -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; -import java.sql.SQLException; -import java.util.*; - -import static com.schbrain.common.constants.ResponseCodeConstants.*; - -/** - * @author liaozan - * @since 2019/10/14 - */ -@Slf4j -@ResponseBody -@ResponseStatus(HttpStatus.OK) -public class DefaultGlobalExceptionHandler implements GlobalExceptionHandler { - - private final List exceptionTranslators; - - public DefaultGlobalExceptionHandler(List exceptionTranslators) { - this.exceptionTranslators = exceptionTranslators; - } - - /************************************* Base Exception Handing *************************************/ - @ExceptionHandler(BaseException.class) - public ResponseDTO handleBaseException(BaseException ex) { - logError(ex); - return buildResponse(ex, ex.getCode(), ex.getAction(), ex.getMessage()); - } - - /************************************* Common Exception Handing *************************************/ - @ExceptionHandler(Throwable.class) - public ResponseDTO handleAll(Throwable ex) { - return loggingThenBuildResponse(ex, SERVER_ERROR); - } - - @ExceptionHandler(NullPointerException.class) - public ResponseDTO handleNullPointerException(NullPointerException ex) { - return loggingThenBuildResponse(ex, SERVER_ERROR); - } - - @ExceptionHandler(IllegalArgumentException.class) - public ResponseDTO handleIllegalArgumentException(IllegalArgumentException ex) { - return loggingThenBuildResponse(ex, SERVER_ERROR); - } - - @ExceptionHandler(IllegalStateException.class) - public ResponseDTO handleIllegalStateException(IllegalStateException ex) { - return loggingThenBuildResponse(ex, SERVER_ERROR); - } - - @ExceptionHandler(NoHandlerFoundException.class) - public ResponseDTO handleNoHandlerFoundException(NoHandlerFoundException ex) { - return loggingThenBuildResponse(ex, PARAM_INVALID); - } - - @ExceptionHandler(AsyncRequestTimeoutException.class) - public ResponseDTO handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex) { - return loggingThenBuildResponse(ex, SERVER_ERROR); - } - - /************************************* SQL Exception Handing *************************************/ - @ExceptionHandler(SQLException.class) - public ResponseDTO handleSQLException(SQLException ex) { - return loggingThenBuildResponse(ex, SERVER_ERROR); - } - - @ExceptionHandler(DataAccessException.class) - public ResponseDTO handleDataAccessException(DataAccessException ex) { - return loggingThenBuildResponse(ex, SERVER_ERROR); - } - - /************************************* Http Request Exception Handing *************************************/ - @ExceptionHandler(HttpRequestMethodNotSupportedException.class) - public ResponseDTO handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) { - String errorMsg = StrUtil.format("不支持该HTTP方法: {}, 请使用 {}", ex.getMethod(), Arrays.toString(ex.getSupportedMethods())); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - @ExceptionHandler(HttpMediaTypeNotSupportedException.class) - public ResponseDTO handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException ex) { - String errorMsg = StrUtil.format("不支持该媒体类型: {}, 请使用 {}", ex.getContentType(), ex.getSupportedMediaTypes()); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - @ExceptionHandler(HttpMediaTypeNotAcceptableException.class) - public ResponseDTO handleHttpMediaTypeNotAcceptableException(HttpMediaTypeNotAcceptableException ex) { - String errorMsg = StrUtil.format("不支持的媒体类型, 请使用 {}", ex.getSupportedMediaTypes()); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - /************************************* Method Parameter Exception Handing *************************************/ - @ExceptionHandler(HttpMessageNotReadableException.class) - public ResponseDTO handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) { - String errorMsg = StrUtil.format("参数解析失败, {}", ex.getMessage()); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - @ExceptionHandler(MethodArgumentTypeMismatchException.class) - public ResponseDTO handlerMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex) { - Object value = ex.getValue(); - String variableName = ex.getName(); - Class requiredTypeClass = ex.getRequiredType(); - String requiredType = ClassUtils.getQualifiedName(requiredTypeClass == null ? Object.class : requiredTypeClass); - String providedType = ClassUtils.getDescriptiveType(value); - String errorMsg = StrUtil.format("参数类型不匹配, 参数名: {}, 需要: {}, 传入: {} 的 {}", variableName, requiredType, providedType, value); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - @ExceptionHandler(MissingPathVariableException.class) - public ResponseDTO handleMissingPathVariableException(MissingPathVariableException ex) { - String errorMsg = StrUtil.format("丢失路径参数, 参数名: {}, 参数类型: {}", ex.getVariableName(), ex.getParameter().getParameterType()); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - @ExceptionHandler(MissingRequestCookieException.class) - public ResponseDTO handleMissingRequestCookieException(MissingRequestCookieException ex) { - String errorMsg = StrUtil.format("丢失Cookie参数, 参数名: {}, 参数类型: {}", ex.getCookieName(), ex.getParameter().getParameterType()); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - @ExceptionHandler(MissingRequestHeaderException.class) - public ResponseDTO handleMissingRequestHeaderException(MissingRequestHeaderException ex) { - String errorMsg = StrUtil.format("丢失Header参数, 参数名: {}, 参数类型: {}", ex.getHeaderName(), ex.getParameter().getParameterType()); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - @ExceptionHandler(MissingServletRequestPartException.class) - public ResponseDTO handleMissingServletRequestPartException(MissingServletRequestPartException ex) { - String errorMsg = StrUtil.format("丢失参数: {}", ex.getRequestPartName()); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - @ExceptionHandler(MissingServletRequestParameterException.class) - public ResponseDTO handleServletRequestParameterException(MissingServletRequestParameterException ex) { - String errorMsg = StrUtil.format("丢失Query参数, 参数名: {}, 参数类型: {}", ex.getParameterName(), ex.getParameterType()); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - @ExceptionHandler(ServletRequestBindingException.class) - public ResponseDTO handleServletRequestBindingException(ServletRequestBindingException ex) { - String errorMsg = StrUtil.format("参数绑定失败: {}", ex.getMessage()); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - /************************************* Parameter Binding Exception Handing *************************************/ - @ExceptionHandler(BindException.class) - public ResponseDTO handleBindException(BindException ex) { - String errorMsg = buildBindingErrorMsg(ex.getBindingResult()); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - @ExceptionHandler(ConstraintViolationException.class) - public ResponseDTO handleConstraintViolationException(ConstraintViolationException ex) { - String errorMsg = buildBindingErrorMsg(ex.getConstraintViolations()); - log.error(errorMsg); - return buildResponse(ex, PARAM_INVALID, errorMsg); - } - - protected ResponseDTO loggingThenBuildResponse(Throwable throwable, int code) { - Throwable rootCause = ExceptionUtil.getRootCause(throwable); - logError(rootCause); - return buildResponse(rootCause, code, rootCause.getMessage()); - } - - protected ResponseDTO buildResponse(Throwable throwable, int code, String message) { - return buildResponse(throwable, code, ResponseActionConstants.ALERT, message); - } - - protected ResponseDTO buildResponse(Throwable throwable, int code, int action, String message) { - ResponseDTO responseDTO = translateException(throwable, code, action, message); - if (responseDTO != null) { - return responseDTO; - } - return ResponseDTO.error(message, code, action); - } - - protected ResponseDTO translateException(Throwable throwable, int code, int action, String message) { - for (ExceptionTranslator exceptionTranslator : exceptionTranslators) { - ResponseDTO responseDTO = exceptionTranslator.translate(throwable, code, action, message); - if (responseDTO != null) { - return responseDTO; - } - } - return null; - } - - protected String buildBindingErrorMsg(BindingResult bindingResult) { - String prefix = "参数验证失败: "; - StringJoiner joiner = new StringJoiner(", "); - for (ObjectError error : bindingResult.getAllErrors()) { - String errorMessage = Optional.ofNullable(error.getDefaultMessage()).orElse("验证失败"); - String source; - if (error instanceof FieldError) { - source = ((FieldError) error).getField(); - } else { - source = error.getObjectName(); - } - joiner.add(source + " " + errorMessage); - } - return prefix + joiner; - } - - protected String buildBindingErrorMsg(Set> constraintViolations) { - String prefix = "参数验证失败: "; - StringJoiner joiner = new StringJoiner(", "); - for (ConstraintViolation violation : constraintViolations) { - PathImpl propertyPath = (PathImpl) violation.getPropertyPath(); - joiner.add(propertyPath.asString() + " " + violation.getMessage()); - } - return prefix + joiner; - } - - protected void logError(Throwable throwable) { - String exMsg = ExceptionUtil.getMessage(throwable); - log.error(exMsg, throwable); - } - -} \ No newline at end of file diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/exception/DefaultGlobalExceptionResolver.java b/commons/web-common/src/main/java/com/schbrain/common/web/exception/DefaultGlobalExceptionResolver.java index 4d71816..c7dd6e4 100644 --- a/commons/web-common/src/main/java/com/schbrain/common/web/exception/DefaultGlobalExceptionResolver.java +++ b/commons/web-common/src/main/java/com/schbrain/common/web/exception/DefaultGlobalExceptionResolver.java @@ -39,7 +39,7 @@ public class DefaultGlobalExceptionResolver extends AbstractHandlerMethodExcepti private final HandlerMethodReturnValueHandlerComposite returnValueHandlerComposite; - private final Map, ExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap<>(64); + private final Map, ExceptionHandlerMethodResolver> exceptionHandlerMethodResolvers = new ConcurrentHashMap<>(64); public DefaultGlobalExceptionResolver(ExceptionHandlerExceptionResolver handlerMethodResolver, WebProperties webProperties, GlobalExceptionHandler exceptionHandler) { this.webProperties = webProperties; @@ -69,7 +69,7 @@ public class DefaultGlobalExceptionResolver extends AbstractHandlerMethodExcepti } @Override - protected final ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) { + protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) { ServletInvocableHandlerMethod exceptionHandlerMethod = createExceptionHandlerMethod(exception, handlerMethod, exceptionHandler); if (exceptionHandlerMethod == null) { return null; @@ -79,7 +79,7 @@ public class DefaultGlobalExceptionResolver extends AbstractHandlerMethodExcepti return doResolveException(webRequest, exceptionHandlerMethod, getArguments(exception, handlerMethod)); } - protected final ModelAndView doResolveException(ServletWebRequest webRequest, ServletInvocableHandlerMethod targetMethod, Object[] arguments) { + protected ModelAndView doResolveException(ServletWebRequest webRequest, ServletInvocableHandlerMethod targetMethod, Object[] arguments) { ModelAndViewContainer mavContainer = new ModelAndViewContainer(); try { targetMethod.invokeAndHandle(webRequest, mavContainer, arguments); @@ -93,6 +93,7 @@ public class DefaultGlobalExceptionResolver extends AbstractHandlerMethodExcepti return null; } + @Nullable protected ServletInvocableHandlerMethod createExceptionHandlerMethod(Exception exception, @Nullable HandlerMethod handlerMethod, Object handler) { Method targetMethod = resolveTargetMethod(exception, handlerMethod); if (targetMethod == null) { @@ -104,6 +105,7 @@ public class DefaultGlobalExceptionResolver extends AbstractHandlerMethodExcepti return exceptionHandlerMethod; } + @Nullable protected Method resolveTargetMethod(Exception exception, @Nullable HandlerMethod handlerMethod) { Method resolvedMethod = null; if (handlerMethod != null) { @@ -122,7 +124,7 @@ public class DefaultGlobalExceptionResolver extends AbstractHandlerMethodExcepti } private ExceptionHandlerMethodResolver getHandlerMethodResolver(Class handlerType) { - return exceptionHandlerCache.computeIfAbsent(handlerType, key -> new ExceptionHandlerMethodResolver(handlerType)); + return exceptionHandlerMethodResolvers.computeIfAbsent(handlerType, key -> new ExceptionHandlerMethodResolver(handlerType)); } /** diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/exception/ExceptionHandlerWebMcvConfigurer.java b/commons/web-common/src/main/java/com/schbrain/common/web/exception/ExceptionHandingWebMvcConfigurer.java similarity index 75% rename from commons/web-common/src/main/java/com/schbrain/common/web/exception/ExceptionHandlerWebMcvConfigurer.java rename to commons/web-common/src/main/java/com/schbrain/common/web/exception/ExceptionHandingWebMvcConfigurer.java index 4c4d470..1410be9 100644 --- a/commons/web-common/src/main/java/com/schbrain/common/web/exception/ExceptionHandlerWebMcvConfigurer.java +++ b/commons/web-common/src/main/java/com/schbrain/common/web/exception/ExceptionHandingWebMvcConfigurer.java @@ -13,13 +13,13 @@ import java.util.List; * @since 2022/8/29 */ @Slf4j -public class ExceptionHandlerWebMcvConfigurer implements WebMvcConfigurer { +public class ExceptionHandingWebMvcConfigurer implements WebMvcConfigurer { private final WebProperties webProperties; private final GlobalExceptionHandler globalExceptionHandler; - public ExceptionHandlerWebMcvConfigurer(WebProperties webProperties, GlobalExceptionHandler globalExceptionHandler) { + public ExceptionHandingWebMvcConfigurer(WebProperties webProperties, GlobalExceptionHandler globalExceptionHandler) { this.webProperties = webProperties; this.globalExceptionHandler = globalExceptionHandler; } @@ -44,15 +44,11 @@ public class ExceptionHandlerWebMcvConfigurer implements WebMvcConfigurer { return; } - addGlobalExceptionResolver(resolvers, adviceExceptionResolver); - } - - protected void addGlobalExceptionResolver(List resolvers, ExceptionHandlerExceptionResolver adviceExceptionResolver) { int index = resolvers.indexOf(adviceExceptionResolver) + 1; - resolvers.add(index, createGlobalExceptionResolver(adviceExceptionResolver)); + resolvers.add(index, createExceptionResolver(adviceExceptionResolver)); } - protected HandlerExceptionResolver createGlobalExceptionResolver(ExceptionHandlerExceptionResolver adviceExceptionResolver) { + protected HandlerExceptionResolver createExceptionResolver(ExceptionHandlerExceptionResolver adviceExceptionResolver) { return new DefaultGlobalExceptionResolver(adviceExceptionResolver, webProperties, globalExceptionHandler); } diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/exception/GlobalExceptionHandler.java b/commons/web-common/src/main/java/com/schbrain/common/web/exception/GlobalExceptionHandler.java index 4138a43..0b6f766 100644 --- a/commons/web-common/src/main/java/com/schbrain/common/web/exception/GlobalExceptionHandler.java +++ b/commons/web-common/src/main/java/com/schbrain/common/web/exception/GlobalExceptionHandler.java @@ -1,11 +1,252 @@ package com.schbrain.common.web.exception; +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.core.util.StrUtil; +import com.schbrain.common.constants.ResponseActionConstants; +import com.schbrain.common.exception.BaseException; +import com.schbrain.common.web.result.ResponseDTO; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.validator.internal.engine.path.PathImpl; +import org.springframework.dao.DataAccessException; +import org.springframework.http.HttpStatus; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.util.ClassUtils; +import org.springframework.validation.*; +import org.springframework.web.*; +import org.springframework.web.bind.*; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.AsyncRequestTimeoutException; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.springframework.web.multipart.support.MissingServletRequestPartException; +import org.springframework.web.servlet.NoHandlerFoundException; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import java.sql.SQLException; +import java.util.*; + +import static com.schbrain.common.constants.ResponseCodeConstants.*; + /** - * 标记接口 - * * @author liaozan - * @since 2022/10/26 + * @since 2019/10/14 */ -public interface GlobalExceptionHandler { +@Slf4j +@ResponseBody +@ResponseStatus(HttpStatus.OK) +public class GlobalExceptionHandler { + + private final List exceptionTranslators; + + public GlobalExceptionHandler(List exceptionTranslators) { + this.exceptionTranslators = exceptionTranslators; + } + + /************************************* Base Exception Handing *************************************/ + @ExceptionHandler(BaseException.class) + public ResponseDTO handleBaseException(BaseException ex) { + logError(ex); + return buildResponse(ex, ex.getCode(), ex.getAction(), ex.getMessage()); + } + + /************************************* Common Exception Handing *************************************/ + @ExceptionHandler(Throwable.class) + public ResponseDTO handleAll(Throwable ex) { + return loggingThenBuildResponse(ex, SERVER_ERROR); + } + + @ExceptionHandler(NullPointerException.class) + public ResponseDTO handleNullPointerException(NullPointerException ex) { + return loggingThenBuildResponse(ex, SERVER_ERROR); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseDTO handleIllegalArgumentException(IllegalArgumentException ex) { + return loggingThenBuildResponse(ex, SERVER_ERROR); + } + + @ExceptionHandler(IllegalStateException.class) + public ResponseDTO handleIllegalStateException(IllegalStateException ex) { + return loggingThenBuildResponse(ex, SERVER_ERROR); + } + + @ExceptionHandler(NoHandlerFoundException.class) + public ResponseDTO handleNoHandlerFoundException(NoHandlerFoundException ex) { + return loggingThenBuildResponse(ex, PARAM_INVALID); + } + + @ExceptionHandler(AsyncRequestTimeoutException.class) + public ResponseDTO handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex) { + return loggingThenBuildResponse(ex, SERVER_ERROR); + } + + /************************************* SQL Exception Handing *************************************/ + @ExceptionHandler(SQLException.class) + public ResponseDTO handleSQLException(SQLException ex) { + return loggingThenBuildResponse(ex, SERVER_ERROR); + } + + @ExceptionHandler(DataAccessException.class) + public ResponseDTO handleDataAccessException(DataAccessException ex) { + return loggingThenBuildResponse(ex, SERVER_ERROR); + } + + /************************************* Http Request Exception Handing *************************************/ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public ResponseDTO handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) { + String errorMsg = StrUtil.format("不支持该HTTP方法: {}, 请使用 {}", ex.getMethod(), Arrays.toString(ex.getSupportedMethods())); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + @ExceptionHandler(HttpMediaTypeNotSupportedException.class) + public ResponseDTO handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException ex) { + String errorMsg = StrUtil.format("不支持该媒体类型: {}, 请使用 {}", ex.getContentType(), ex.getSupportedMediaTypes()); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + @ExceptionHandler(HttpMediaTypeNotAcceptableException.class) + public ResponseDTO handleHttpMediaTypeNotAcceptableException(HttpMediaTypeNotAcceptableException ex) { + String errorMsg = StrUtil.format("不支持的媒体类型, 请使用 {}", ex.getSupportedMediaTypes()); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + /************************************* Method Parameter Exception Handing *************************************/ + @ExceptionHandler(HttpMessageNotReadableException.class) + public ResponseDTO handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) { + String errorMsg = StrUtil.format("参数解析失败, {}", ex.getMessage()); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public ResponseDTO handlerMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex) { + Object value = ex.getValue(); + String variableName = ex.getName(); + Class requiredTypeClass = ex.getRequiredType(); + String requiredType = ClassUtils.getQualifiedName(requiredTypeClass == null ? Object.class : requiredTypeClass); + String providedType = ClassUtils.getDescriptiveType(value); + String errorMsg = StrUtil.format("参数类型不匹配, 参数名: {}, 需要: {}, 传入: {} 的 {}", variableName, requiredType, providedType, value); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + @ExceptionHandler(MissingPathVariableException.class) + public ResponseDTO handleMissingPathVariableException(MissingPathVariableException ex) { + String errorMsg = StrUtil.format("丢失路径参数, 参数名: {}, 参数类型: {}", ex.getVariableName(), ex.getParameter().getParameterType()); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + @ExceptionHandler(MissingRequestCookieException.class) + public ResponseDTO handleMissingRequestCookieException(MissingRequestCookieException ex) { + String errorMsg = StrUtil.format("丢失Cookie参数, 参数名: {}, 参数类型: {}", ex.getCookieName(), ex.getParameter().getParameterType()); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + @ExceptionHandler(MissingRequestHeaderException.class) + public ResponseDTO handleMissingRequestHeaderException(MissingRequestHeaderException ex) { + String errorMsg = StrUtil.format("丢失Header参数, 参数名: {}, 参数类型: {}", ex.getHeaderName(), ex.getParameter().getParameterType()); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + @ExceptionHandler(MissingServletRequestPartException.class) + public ResponseDTO handleMissingServletRequestPartException(MissingServletRequestPartException ex) { + String errorMsg = StrUtil.format("丢失参数: {}", ex.getRequestPartName()); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + @ExceptionHandler(MissingServletRequestParameterException.class) + public ResponseDTO handleServletRequestParameterException(MissingServletRequestParameterException ex) { + String errorMsg = StrUtil.format("丢失Query参数, 参数名: {}, 参数类型: {}", ex.getParameterName(), ex.getParameterType()); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + @ExceptionHandler(ServletRequestBindingException.class) + public ResponseDTO handleServletRequestBindingException(ServletRequestBindingException ex) { + String errorMsg = StrUtil.format("参数绑定失败: {}", ex.getMessage()); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + /************************************* Parameter Binding Exception Handing *************************************/ + @ExceptionHandler(BindException.class) + public ResponseDTO handleBindException(BindException ex) { + String errorMsg = buildBindingErrorMsg(ex.getBindingResult()); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + @ExceptionHandler(ConstraintViolationException.class) + public ResponseDTO handleConstraintViolationException(ConstraintViolationException ex) { + String errorMsg = buildBindingErrorMsg(ex.getConstraintViolations()); + log.error(errorMsg); + return buildResponse(ex, PARAM_INVALID, errorMsg); + } + + private ResponseDTO loggingThenBuildResponse(Throwable throwable, int code) { + Throwable rootCause = ExceptionUtil.getRootCause(throwable); + logError(rootCause); + return buildResponse(rootCause, code, rootCause.getMessage()); + } + + private ResponseDTO buildResponse(Throwable throwable, int code, String message) { + return buildResponse(throwable, code, ResponseActionConstants.ALERT, message); + } + + private ResponseDTO buildResponse(Throwable throwable, int code, int action, String message) { + ResponseDTO responseDTO = translateException(throwable, code, action, message); + if (responseDTO != null) { + return responseDTO; + } + return ResponseDTO.error(message, code, action); + } + + private ResponseDTO translateException(Throwable throwable, int code, int action, String message) { + for (ExceptionTranslator exceptionTranslator : exceptionTranslators) { + ResponseDTO responseDTO = exceptionTranslator.translate(throwable, code, action, message); + if (responseDTO != null) { + return responseDTO; + } + } + return null; + } + + private String buildBindingErrorMsg(BindingResult bindingResult) { + String prefix = "参数验证失败: "; + StringJoiner joiner = new StringJoiner(", "); + for (ObjectError error : bindingResult.getAllErrors()) { + String errorMessage = Optional.ofNullable(error.getDefaultMessage()).orElse("验证失败"); + String source; + if (error instanceof FieldError) { + source = ((FieldError) error).getField(); + } else { + source = error.getObjectName(); + } + joiner.add(source + " " + errorMessage); + } + return prefix + joiner; + } + + private String buildBindingErrorMsg(Set> constraintViolations) { + String prefix = "参数验证失败: "; + StringJoiner joiner = new StringJoiner(", "); + for (ConstraintViolation violation : constraintViolations) { + PathImpl propertyPath = (PathImpl) violation.getPropertyPath(); + joiner.add(propertyPath.asString() + " " + violation.getMessage()); + } + return prefix + joiner; + } + + private void logError(Throwable throwable) { + String exMsg = ExceptionUtil.getMessage(throwable); + log.error(exMsg, throwable); + } } \ No newline at end of file -- GitLab