From f2aab043150636d1031e469c48c50c94d6acca3f Mon Sep 17 00:00:00 2001 From: qinyujie3 Date: Sat, 13 May 2023 02:09:07 +0800 Subject: [PATCH] =?UTF-8?q?3.0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../gracefulresponse/AutoConfig.java | 7 + .../ExceptionAliasRegister.java | 5 +- .../GracefulResponseProperties.java | 38 +++-- .../advice/GlobalExceptionAdvice.java | 4 +- .../advice/ValidationExceptionAdvice.java | 141 ++++++++++++++++++ .../api/ExceptionAliasFor.java | 11 +- .../api/ResponseStatusFactory.java | 2 +- .../api/ValidationStatusCode.java | 21 +++ .../defaults/DefaultConstants.java | 28 ++++ .../defaults/DefaultResponseFactory.java | 2 +- .../DefaultResponseStatusFactoryImpl.java | 6 +- 12 files changed, 242 insertions(+), 25 deletions(-) create mode 100644 src/main/java/com/feiniaojin/gracefulresponse/advice/ValidationExceptionAdvice.java create mode 100644 src/main/java/com/feiniaojin/gracefulresponse/api/ValidationStatusCode.java create mode 100644 src/main/java/com/feiniaojin/gracefulresponse/defaults/DefaultConstants.java diff --git a/pom.xml b/pom.xml index c032ec2..acb6f5c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.feiniaojin graceful-response - 2.1 + 3.0 Graceful Response Help Spring Boot web project responses gracefully! https://github.com/feiniaojin/graceful-response.git diff --git a/src/main/java/com/feiniaojin/gracefulresponse/AutoConfig.java b/src/main/java/com/feiniaojin/gracefulresponse/AutoConfig.java index babc9c6..4310cc0 100644 --- a/src/main/java/com/feiniaojin/gracefulresponse/AutoConfig.java +++ b/src/main/java/com/feiniaojin/gracefulresponse/AutoConfig.java @@ -3,6 +3,7 @@ import com.feiniaojin.gracefulresponse.advice.GlobalExceptionAdvice; import com.feiniaojin.gracefulresponse.advice.NotVoidResponseBodyAdvice; +import com.feiniaojin.gracefulresponse.advice.ValidationExceptionAdvice; import com.feiniaojin.gracefulresponse.advice.VoidResponseBodyAdvice; import com.feiniaojin.gracefulresponse.api.ResponseFactory; import com.feiniaojin.gracefulresponse.api.ResponseStatusFactory; @@ -30,6 +31,12 @@ public GlobalExceptionAdvice globalExceptionAdvice() { return new GlobalExceptionAdvice(); } + @Bean + @ConditionalOnMissingBean(value = ValidationExceptionAdvice.class) + public ValidationExceptionAdvice validationAdvice() { + return new ValidationExceptionAdvice(); + } + @Bean @ConditionalOnMissingBean(NotVoidResponseBodyAdvice.class) public NotVoidResponseBodyAdvice notVoidResponseBodyAdvice() { diff --git a/src/main/java/com/feiniaojin/gracefulresponse/ExceptionAliasRegister.java b/src/main/java/com/feiniaojin/gracefulresponse/ExceptionAliasRegister.java index f0f857d..c51a1e6 100644 --- a/src/main/java/com/feiniaojin/gracefulresponse/ExceptionAliasRegister.java +++ b/src/main/java/com/feiniaojin/gracefulresponse/ExceptionAliasRegister.java @@ -26,7 +26,10 @@ public ExceptionAliasRegister doRegisterExceptionAlias(Class[] classes = exceptionAliasFor.aliasFor(); + for (Class c : classes) { + aliasForMap.put(c, exceptionAliasFor); + } return this; } diff --git a/src/main/java/com/feiniaojin/gracefulresponse/GracefulResponseProperties.java b/src/main/java/com/feiniaojin/gracefulresponse/GracefulResponseProperties.java index 2cc1af0..a2194db 100644 --- a/src/main/java/com/feiniaojin/gracefulresponse/GracefulResponseProperties.java +++ b/src/main/java/com/feiniaojin/gracefulresponse/GracefulResponseProperties.java @@ -1,5 +1,6 @@ package com.feiniaojin.gracefulresponse; +import com.feiniaojin.gracefulresponse.defaults.DefaultConstants; import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -31,22 +32,27 @@ public class GracefulResponseProperties { /** * 默认的成功返回码 */ - private String defaultSuccessCode = "0"; + private String defaultSuccessCode = DefaultConstants.DEFAULT_SUCCESS_CODE; /** * 默认的成功提示 */ - private String defaultSuccessMsg = "ok"; + private String defaultSuccessMsg = DefaultConstants.DEFAULT_SUCCESS_MSG; /** * 默认的失败码 */ - private String defaultFailCode = "1"; + private String defaultErrorCode = DefaultConstants.DEFAULT_ERROR_CODE; /** * 默认的失败提示 */ - private String defaultFailMsg = "error"; + private String defaultErrorMsg = DefaultConstants.DEFAULT_ERROR_MSG; + + /** + * Validate异常码,不提供的话默认DefaultConstants.DEFAULT_ERROR_CODE + */ + private String defaultValidateErrorCode = DefaultConstants.DEFAULT_ERROR_CODE; public boolean isUseValidationMsg() { return useValidationMsg; @@ -80,20 +86,20 @@ public void setDefaultSuccessMsg(String defaultSuccessMsg) { this.defaultSuccessMsg = defaultSuccessMsg; } - public String getDefaultFailCode() { - return defaultFailCode; + public String getDefaultErrorCode() { + return defaultErrorCode; } - public void setDefaultFailCode(String defaultFailCode) { - this.defaultFailCode = defaultFailCode; + public void setDefaultErrorCode(String defaultErrorCode) { + this.defaultErrorCode = defaultErrorCode; } - public String getDefaultFailMsg() { - return defaultFailMsg; + public String getDefaultErrorMsg() { + return defaultErrorMsg; } - public void setDefaultFailMsg(String defaultFailMsg) { - this.defaultFailMsg = defaultFailMsg; + public void setDefaultErrorMsg(String defaultErrorMsg) { + this.defaultErrorMsg = defaultErrorMsg; } public String getResponseClassFullName() { @@ -111,4 +117,12 @@ public Integer getResponseStyle() { public void setResponseStyle(Integer responseStyle) { this.responseStyle = responseStyle; } + + public String getDefaultValidateErrorCode() { + return defaultValidateErrorCode; + } + + public void setDefaultValidateErrorCode(String defaultValidateErrorCode) { + this.defaultValidateErrorCode = defaultValidateErrorCode; + } } diff --git a/src/main/java/com/feiniaojin/gracefulresponse/advice/GlobalExceptionAdvice.java b/src/main/java/com/feiniaojin/gracefulresponse/advice/GlobalExceptionAdvice.java index 78739e6..148a617 100644 --- a/src/main/java/com/feiniaojin/gracefulresponse/advice/GlobalExceptionAdvice.java +++ b/src/main/java/com/feiniaojin/gracefulresponse/advice/GlobalExceptionAdvice.java @@ -14,6 +14,7 @@ import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.core.annotation.Order; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @@ -28,6 +29,7 @@ * @since 0.1 */ @ControllerAdvice +@Order(200) public class GlobalExceptionAdvice implements ApplicationContextAware { private final Logger logger = LoggerFactory.getLogger(GlobalExceptionAdvice.class); @@ -89,7 +91,7 @@ private ResponseStatus fromExceptionClass(Class clazz) { } } - return responseStatusFactory.defaultFail(); + return responseStatusFactory.defaultError(); } @Override diff --git a/src/main/java/com/feiniaojin/gracefulresponse/advice/ValidationExceptionAdvice.java b/src/main/java/com/feiniaojin/gracefulresponse/advice/ValidationExceptionAdvice.java new file mode 100644 index 0000000..6c8fa8b --- /dev/null +++ b/src/main/java/com/feiniaojin/gracefulresponse/advice/ValidationExceptionAdvice.java @@ -0,0 +1,141 @@ +package com.feiniaojin.gracefulresponse.advice; + +import com.feiniaojin.gracefulresponse.GracefulResponseProperties; +import com.feiniaojin.gracefulresponse.api.ResponseFactory; +import com.feiniaojin.gracefulresponse.api.ResponseStatusFactory; +import com.feiniaojin.gracefulresponse.api.ValidationStatusCode; +import com.feiniaojin.gracefulresponse.data.Response; +import com.feiniaojin.gracefulresponse.data.ResponseStatus; +import org.springframework.core.annotation.Order; +import org.springframework.validation.BindException; +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerExecutionChain; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import javax.annotation.Resource; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.ValidationException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@ControllerAdvice +@Order(100) +public class ValidationExceptionAdvice { + + @Resource + private RequestMappingHandlerMapping mapping; + + @Resource + private ResponseStatusFactory responseStatusFactory; + + @Resource + private ResponseFactory responseFactory; + + @Resource + private GracefulResponseProperties gracefulResponseProperties; + + @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class}) + @ResponseBody + public Response exceptionHandler(Exception e) throws Exception { + + if (e instanceof BindException) { + ResponseStatus responseStatus = this.fromBindException(e); + return responseFactory.newInstance(responseStatus); + } + + if (e instanceof MethodArgumentNotValidException) { + ResponseStatus responseStatus = this.fromMethodArgumentNotValidException(e); + return responseFactory.newInstance(responseStatus); + } + + if (e instanceof ConstraintViolationException) { + ResponseStatus responseStatus = this.fromConstraintViolationException(e); + return responseFactory.newInstance(responseStatus); + } + + return responseFactory.newFailInstance(); + } + + private ResponseStatus fromMethodArgumentNotValidException(Exception e) throws Exception { + MethodArgumentNotValidException me = (MethodArgumentNotValidException) e; + List allErrors = me.getBindingResult().getAllErrors(); + String msg = allErrors.stream().map(s -> s.getDefaultMessage()).collect(Collectors.joining(";")); + String code = this.determineErrorCode(); + return responseStatusFactory.newInstance(code, msg); + } + + private String determineErrorCode() throws Exception { + Method method = this.currentControllerMethod(); + ValidationStatusCode validateStatusCode = method.getAnnotation(ValidationStatusCode.class); + if (validateStatusCode == null) { + validateStatusCode = method.getDeclaringClass().getAnnotation(ValidationStatusCode.class); + } + if (validateStatusCode != null) { + return validateStatusCode.code(); + } + return gracefulResponseProperties.getDefaultValidateErrorCode(); + } + + private Method currentControllerMethod() throws Exception { + RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); + ServletRequestAttributes sra = (ServletRequestAttributes) requestAttributes; + HandlerExecutionChain handlerChain = mapping.getHandler(sra.getRequest()); + HandlerMethod handler = (HandlerMethod) handlerChain.getHandler(); + Method method = handler.getMethod(); + return method; + } + + private ResponseStatus fromConstraintViolationException(Exception e) throws Exception { + + ConstraintViolationException exception = (ConstraintViolationException) e; + Set> violationSet = exception.getConstraintViolations(); + String msg = violationSet.stream().map(s -> s.getConstraintDescriptor().getMessageTemplate()).collect(Collectors.joining(";")); + String code = this.determineErrorCode(); + return responseStatusFactory.newInstance(code, msg); + } + + private ResponseStatus fromBindException(Exception e) throws NoSuchFieldException { + + String code; + + BindException bindException = (BindException) e; + FieldError fieldError = bindException.getFieldError(); + String fieldName = fieldError.getField(); + Object target = bindException.getTarget(); + Field declaredField = target.getClass().getDeclaredField(fieldName); + declaredField.setAccessible(true); + ValidationStatusCode annotation = declaredField.getAnnotation(ValidationStatusCode.class); + declaredField.setAccessible(false); + + //属性上找不到注解,尝试获取类上的注解 + if (annotation == null) { + annotation = target.getClass().getAnnotation(ValidationStatusCode.class); + } + + if (annotation != null) { + code = annotation.code(); + } else { + code = gracefulResponseProperties.getDefaultValidateErrorCode(); + } + + String msg = bindException.getAllErrors() + .stream().map(s -> s.getDefaultMessage()) + .collect(Collectors.joining(";")); + + return responseStatusFactory.newInstance(code, msg); + } + +} diff --git a/src/main/java/com/feiniaojin/gracefulresponse/api/ExceptionAliasFor.java b/src/main/java/com/feiniaojin/gracefulresponse/api/ExceptionAliasFor.java index bac793a..cd62905 100644 --- a/src/main/java/com/feiniaojin/gracefulresponse/api/ExceptionAliasFor.java +++ b/src/main/java/com/feiniaojin/gracefulresponse/api/ExceptionAliasFor.java @@ -1,8 +1,9 @@ package com.feiniaojin.gracefulresponse.api; -import java.lang.annotation.*; +import com.feiniaojin.gracefulresponse.defaults.DefaultConstants; +import java.lang.annotation.*; /** * 异常映射别名,把某个异常设置为外部异常的别名,以便自定义错误码和提示信息 @@ -21,19 +22,19 @@ * * @return 异常对应的错误码 */ - String code() default "ERROR"; + String code() default DefaultConstants.DEFAULT_ERROR_CODE; /** * 异常信息. * * @return 异常对应的提示信息 */ - String msg() default "Poor network quality!"; + String msg() default DefaultConstants.DEFAULT_ERROR_MSG; /** - * 作为某个异常的别名 + * 作为某些异常的别名 * * @return */ - Class aliasFor(); + Class[] aliasFor(); } diff --git a/src/main/java/com/feiniaojin/gracefulresponse/api/ResponseStatusFactory.java b/src/main/java/com/feiniaojin/gracefulresponse/api/ResponseStatusFactory.java index ecef3ab..8f8fcb8 100644 --- a/src/main/java/com/feiniaojin/gracefulresponse/api/ResponseStatusFactory.java +++ b/src/main/java/com/feiniaojin/gracefulresponse/api/ResponseStatusFactory.java @@ -19,7 +19,7 @@ public interface ResponseStatusFactory { * * @return */ - ResponseStatus defaultFail(); + ResponseStatus defaultError(); /** diff --git a/src/main/java/com/feiniaojin/gracefulresponse/api/ValidationStatusCode.java b/src/main/java/com/feiniaojin/gracefulresponse/api/ValidationStatusCode.java new file mode 100644 index 0000000..dc3b48c --- /dev/null +++ b/src/main/java/com/feiniaojin/gracefulresponse/api/ValidationStatusCode.java @@ -0,0 +1,21 @@ +package com.feiniaojin.gracefulresponse.api; + +import com.feiniaojin.gracefulresponse.defaults.DefaultConstants; + +import java.lang.annotation.*; + +/** + * 指定参数校验的异常码 + */ +@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ValidationStatusCode { + + /** + * 异常对应的错误码. + * + * @return 异常对应的错误码 + */ + String code() default DefaultConstants.DEFAULT_ERROR_CODE; +} diff --git a/src/main/java/com/feiniaojin/gracefulresponse/defaults/DefaultConstants.java b/src/main/java/com/feiniaojin/gracefulresponse/defaults/DefaultConstants.java new file mode 100644 index 0000000..c1dea10 --- /dev/null +++ b/src/main/java/com/feiniaojin/gracefulresponse/defaults/DefaultConstants.java @@ -0,0 +1,28 @@ +package com.feiniaojin.gracefulresponse.defaults; + +/** + * 默认的响应码和提示信息 + */ +public class DefaultConstants { + + /** + * 默认的成功响应码 + */ + public static final String DEFAULT_SUCCESS_CODE = "0"; + + /** + * 默认的成功提示信息 + */ + public static final String DEFAULT_SUCCESS_MSG = "ok"; + + /** + * 默认的错误码 + */ + public static final String DEFAULT_ERROR_CODE = "1"; + + /** + * 默认的错误提示 + */ + public static final String DEFAULT_ERROR_MSG = "error"; + +} diff --git a/src/main/java/com/feiniaojin/gracefulresponse/defaults/DefaultResponseFactory.java b/src/main/java/com/feiniaojin/gracefulresponse/defaults/DefaultResponseFactory.java index 850e1b8..8c62a08 100644 --- a/src/main/java/com/feiniaojin/gracefulresponse/defaults/DefaultResponseFactory.java +++ b/src/main/java/com/feiniaojin/gracefulresponse/defaults/DefaultResponseFactory.java @@ -87,7 +87,7 @@ public Response newSuccessInstance(Object payload) { @Override public Response newFailInstance() { Response bean = this.newEmptyInstance(); - bean.setStatus(responseStatusFactory.defaultFail()); + bean.setStatus(responseStatusFactory.defaultError()); return bean; } diff --git a/src/main/java/com/feiniaojin/gracefulresponse/defaults/DefaultResponseStatusFactoryImpl.java b/src/main/java/com/feiniaojin/gracefulresponse/defaults/DefaultResponseStatusFactoryImpl.java index dbf0de9..12f7e98 100644 --- a/src/main/java/com/feiniaojin/gracefulresponse/defaults/DefaultResponseStatusFactoryImpl.java +++ b/src/main/java/com/feiniaojin/gracefulresponse/defaults/DefaultResponseStatusFactoryImpl.java @@ -28,10 +28,10 @@ public ResponseStatus defaultSuccess() { } @Override - public ResponseStatus defaultFail() { + public ResponseStatus defaultError() { DefaultResponseStatus defaultResponseStatus = new DefaultResponseStatus(); - defaultResponseStatus.setCode(properties.getDefaultFailCode()); - defaultResponseStatus.setMsg(properties.getDefaultFailMsg()); + defaultResponseStatus.setCode(properties.getDefaultErrorCode()); + defaultResponseStatus.setMsg(properties.getDefaultErrorMsg()); return defaultResponseStatus; }