Pre Merge pull request !35 from forest羽/auto_validate_response

This commit is contained in:
forest羽 2021-05-29 16:59:25 +08:00 committed by Gitee
commit dc748c97cd
15 changed files with 390 additions and 424 deletions

View File

@ -0,0 +1,83 @@
package cn.iocoder.dashboard.framework.validator.custom;
import javax.validation.ConstraintTarget;
import javax.validation.ConstraintValidator;
import javax.validation.Payload;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.ValidateUnwrappedValue;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* <p>自定义约束描述符
*
* @author zzf
* @date 2021/5/19
*/
public class CustomConstraintDescriptor<R extends Annotation> implements ConstraintDescriptor<R> {
private final R annotation;
public CustomConstraintDescriptor(R annotation) {
this.annotation = annotation;
}
@Override
public R getAnnotation() {
return annotation;
}
@Override
public String getMessageTemplate() {
return null;
}
@Override
public Set<Class<?>> getGroups() {
return null;
}
@Override
public Set<Class<? extends Payload>> getPayload() {
return null;
}
@Override
public ConstraintTarget getValidationAppliesTo() {
return null;
}
@Override
public List<Class<? extends ConstraintValidator<R, ?>>> getConstraintValidatorClasses() {
return null;
}
@Override
public Map<String, Object> getAttributes() {
return new HashMap<>();
}
@Override
public Set<ConstraintDescriptor<?>> getComposingConstraints() {
return null;
}
@Override
public boolean isReportAsSingleViolation() {
return false;
}
@Override
public ValidateUnwrappedValue getValueUnwrapping() {
return null;
}
@Override
public <U> U unwrap(Class<U> type) {
return null;
}
}

View File

@ -0,0 +1,123 @@
package cn.iocoder.dashboard.framework.validator.custom;
import lombok.Setter;
import org.hibernate.validator.internal.engine.path.PathImpl;
import javax.validation.ConstraintViolation;
import javax.validation.Path;
import javax.validation.metadata.ConstraintDescriptor;
/**
* <p>校验不通过的返回值对象
*
* @author zzf
* @date 2021/5/19
* <T> 需要校验的方法所在的类
*/
public class CustomConstraintViolation<T> implements ConstraintViolation<T> {
@Setter
private String message;
@Setter
private T rootBean;
@Setter
private Path propertyPath;
@Setter
private Object invalidValue;
@Setter
private ConstraintDescriptor<?> constraintDescriptor;
@Override
public String getMessage() {
return message;
}
@Override
public String getMessageTemplate() {
return null;
}
@Override
public T getRootBean() {
return rootBean;
}
@Override
public Class<T> getRootBeanClass() {
return null;
}
@Override
public Object getLeafBean() {
return null;
}
@Override
public Object[] getExecutableParameters() {
return new Object[0];
}
@Override
public Object getExecutableReturnValue() {
return null;
}
@Override
public Path getPropertyPath() {
return propertyPath;
}
@Override
public Object getInvalidValue() {
return invalidValue;
}
@Override
public ConstraintDescriptor<?> getConstraintDescriptor() {
return constraintDescriptor;
}
@Override
public <U> U unwrap(Class<U> type) {
return null;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
/**
* 校验字段值是否合法
* @param rootBean 需要校验的方法所在的类对象
* @param msg 校验错误信息
* @param path 属性路径
* @param invalidValue 校验失败的属性值
* @param constraintDescriptor CustomConstraintDescriptor
* @param <T> 泛型
* @return CustomConstraintViolation
*/
public static <T> CustomConstraintViolation<T> of(T rootBean,
String msg,
String path,
Object invalidValue,
ConstraintDescriptor<?> constraintDescriptor) {
CustomConstraintViolation<T> constraintViolation = new CustomConstraintViolation<>();
constraintViolation.setMessage(msg);
constraintViolation.setRootBean(rootBean);
constraintViolation.setPropertyPath(PathImpl.createPathFromString(path));
constraintViolation.setInvalidValue(invalidValue);
constraintViolation.setConstraintDescriptor(constraintDescriptor);
return constraintViolation;
}
}

View File

@ -1,7 +1,6 @@
package cn.iocoder.dashboard.framework.validator.custom;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.dashboard.framework.validator.custom.handler.ValidateHandlerHelper;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.ApiModelProperty;
import lombok.SneakyThrows;

View File

@ -1,137 +0,0 @@
package cn.iocoder.dashboard.framework.validator.custom;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.helpers.MessageFormatter;
import javax.validation.constraints.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Date;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* 校验注解枚举
*
* @author zzf
* @date 2021/4/13 11:35
*/
@Slf4j
@Getter
@AllArgsConstructor
public enum ValidateAnnotationEnum {
NOT_NULL(NotNull.class,
"必传!",
(object, param) -> {
return ObjectUtil.isNotNull(object);
}
),
NOT_EMPTY(NotEmpty.class,
"内容不能为空!",
(object, param) -> {
return ObjectUtil.isNotEmpty(object);
}
),
NOT_BLANK(NotBlank.class,
"内容需包含有效字符!",
(object, param) -> {
if (object instanceof String) {
return StrUtil.isNotBlank(String.valueOf(object));
}
return false;
}
),
MAX(true,
Max.class,
"不能超过{}",
(object, param) -> {
if (object instanceof Integer) {
return param > (Integer) object;
}
if (object instanceof BigDecimal | object instanceof Double | object instanceof Short) {
return new BigDecimal(String.valueOf(param)).compareTo(new BigDecimal(String.valueOf(object))) > 0;
}
if (object instanceof Long) {
return param > (Long) object;
}
if (object instanceof Date) {
return param > ((Date) object).getTime();
}
return false;
}
),
MIN(true,
Min.class,
"不能小于{}",
(object, param) -> {
if (object instanceof Integer) {
return param < (Integer) object;
}
if (object instanceof BigDecimal | object instanceof Double | object instanceof Short) {
return new BigDecimal(String.valueOf(param)).compareTo(new BigDecimal(String.valueOf(object))) < 0;
}
if (object instanceof Long) {
return param < (Long) object;
}
if (object instanceof Date) {
return param < ((Date) object).getTime();
}
return false;
});
private final boolean hadParam;
private final Class<? extends Annotation> annotation;
private final String msg;
private final BiFunction<Object, Long, Boolean> checkFunction;
ValidateAnnotationEnum(Class<? extends Annotation> annotation, String msg, BiFunction<Object, Long, Boolean> checkFunction) {
this.hadParam = false;
this.annotation = annotation;
this.msg = msg;
this.checkFunction = checkFunction;
}
public String formatMsg(String fieldName, String... param) {
if (hadParam) {
return fieldName + MessageFormatter.arrayFormat(msg, param).getMessage();
}
return fieldName + msg;
}
public boolean checkParam(Field field, Object object, Long... param) throws IllegalAccessException {
if (hadParam && param != null && param.length == 1) {
return checkFunction.apply(field.get(object), param[0]);
}
if (!hadParam) {
return checkFunction.apply(field.get(object), null);
}
log.error("[参数校验失败!] Enum = {}, TargetClass = {}, FieldName = {}, param = {}",
this.toString(),
object.getClass().getSimpleName(),
field.getName(),
param);
return true;
}
public interface IfmFunction {
Boolean apply(Field field, Object object, Function<Object, Boolean> isValid);
}
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.dashboard.framework.validator.custom.handler;
package cn.iocoder.dashboard.framework.validator.custom;
import cn.hutool.core.util.ReflectUtil;
@ -17,6 +17,7 @@ public interface ValidateAnnotationHandler<T extends Annotation> {
/**
* 获取实现类的具体的注解类对象
* @return T
*/
Class<T> getAnnotation();

View File

@ -0,0 +1,111 @@
package cn.iocoder.dashboard.framework.validator.custom;
import cn.hutool.core.collection.CollectionUtil;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.*;
import java.lang.reflect.Field;
import java.util.*;
/**
* 校验处理辅助类
*
* @author zzf
* @date 2021/4/15 9:49
*/
public class ValidateHandlerHelper {
private static Set<ValidateAnnotationHandler<?>> handlerSet = new HashSet<>(10);
static {
ServiceLoader.load(ValidateAnnotationHandler.class).forEach(handlerSet::add);
}
/**
* 校验指定字段值是否合法
* <p>
* 这里针对集合类型字段做递归校验
* 实现深层对象参数值校验
*
* @param field 字段对象
* @param paramObject 需要校验的字段所在的对象
* @param rootBean 需要校验的方法所在的类对象
* @param propertyPath 属性路径
* @param <T> 需要校验的方法所在的类
* @return 校验结果集
*/
public static <T> Set<ConstraintViolation<T>> validate(Field field,
Object paramObject,
T rootBean,
String propertyPath) {
Set<ConstraintViolation<T>> result = new HashSet<>();
Object fieldValue;
try {
field.setAccessible(true);
fieldValue = field.get(paramObject);
} catch (IllegalAccessException e) {
return result;
}
if (fieldValue instanceof Collection) {
Collection<?> collectionFieldValue = (Collection<?>) fieldValue;
if (CollectionUtil.isNotEmpty(collectionFieldValue)) {
for (Object collectionElement : collectionFieldValue) {
Field[] fields = collectionElement.getClass().getDeclaredFields();
for (Field collectionFieldElementField : fields) {
result.addAll(
validate(
collectionFieldElementField,
collectionElement,
rootBean,
propertyPath + "." + collectionFieldElementField.getName()
)
);
}
}
}
}
CustomConstraintViolation<T> violation = validateField(field, paramObject, rootBean, propertyPath, fieldValue);
if (violation != null) {
result.add(violation);
}
return result;
}
/**
* 校验字段值如果值合法返回null不合法返回消息内容
*
* @param field 需要校验的字段
* @param targetObject 字段所在的对象
* @return 值合法返回null不合法返回消息内容
*/
private static <T> CustomConstraintViolation<T> validateField(Field field,
Object targetObject,
T rootBean,
String propertyPath,
Object fieldValue) {
Optional<ValidateAnnotationHandler<?>> handlerOptional =
handlerSet.stream().filter(s -> s.isAnnotationPresent(field)).findFirst();
if (handlerOptional.isPresent()) {
String validate = handlerOptional.get().validate(field, targetObject);
if (validate != null) {
String fieldComment;
if (field.isAnnotationPresent(ApiModelProperty.class)) {
ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
fieldComment = annotation.value();
} else {
fieldComment = field.getName();
}
String msg = fieldComment + validate;
CustomConstraintDescriptor<?> customConstraintDescriptor =
new CustomConstraintDescriptor<>(field.getAnnotation(handlerOptional.get().getAnnotation()));
return CustomConstraintViolation.of(rootBean, msg, propertyPath, fieldValue, customConstraintDescriptor);
}
}
return null;
}
}

View File

@ -1,10 +1,12 @@
package cn.iocoder.dashboard.framework.validator.custom.handler;
import cn.iocoder.dashboard.framework.validator.custom.ValidateAnnotationHandler;
import org.slf4j.helpers.MessageFormatter;
import javax.validation.constraints.Max;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;
/**
* 最大值校验处理类
@ -23,6 +25,10 @@ public class MaxHandler implements ValidateAnnotationHandler<Max> {
@Override
public String validate(Max validateAnnotation, Object fieldValue) {
if (Objects.isNull(fieldValue)) {
return null;
}
long value = validateAnnotation.value();
boolean valid;
if (fieldValue instanceof Integer) {

View File

@ -1,10 +1,12 @@
package cn.iocoder.dashboard.framework.validator.custom.handler;
import cn.iocoder.dashboard.framework.validator.custom.ValidateAnnotationHandler;
import org.slf4j.helpers.MessageFormatter;
import javax.validation.constraints.Min;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;
/**
* 最小值校验处理类
@ -23,6 +25,10 @@ public class MinHandler implements ValidateAnnotationHandler<Min> {
@Override
public String validate(Min validateAnnotation, Object fieldValue) {
if (Objects.isNull(fieldValue)) {
return null;
}
long value = validateAnnotation.value();
boolean valid;
if (fieldValue instanceof Integer) {

View File

@ -1,6 +1,7 @@
package cn.iocoder.dashboard.framework.validator.custom.handler;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.dashboard.framework.validator.custom.ValidateAnnotationHandler;
import javax.validation.constraints.NotBlank;

View File

@ -1,6 +1,7 @@
package cn.iocoder.dashboard.framework.validator.custom.handler;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.dashboard.framework.validator.custom.ValidateAnnotationHandler;
import javax.validation.constraints.NotEmpty;

View File

@ -1,7 +1,7 @@
package cn.iocoder.dashboard.framework.validator.custom.handler;
import cn.hutool.core.util.ObjectUtil;
import org.springframework.beans.factory.annotation.Value;
import cn.iocoder.dashboard.framework.validator.custom.ValidateAnnotationHandler;
import javax.validation.constraints.NotNull;

View File

@ -0,0 +1,48 @@
package cn.iocoder.dashboard.framework.validator.custom.handler;
import cn.iocoder.dashboard.framework.validator.custom.ValidateAnnotationHandler;
import org.slf4j.helpers.MessageFormatter;
import javax.validation.constraints.Size;
import java.util.Objects;
/**
* <p>长度校验处理类
*
* @author xyf
* @date 2021/5/13
*/
public class SizeHandler implements ValidateAnnotationHandler<Size> {
/**
* 小于等于
*/
private static final String VALUE_LT = "长度必须大于等于{}!";
/**
* 大于等于
*/
private static final String VALUE_GT = "长度必须小于等于{}!";
@Override
public Class<Size> getAnnotation() {
return Size.class;
}
@Override
public String validate(Size validateAnnotation, Object fieldValue) {
if (Objects.isNull(fieldValue)) {
return null;
}
if (fieldValue.toString().length() > validateAnnotation.max()) {
return MessageFormatter.format(VALUE_GT, fieldValue).getMessage();
}
if (fieldValue.toString().length() < validateAnnotation.min()) {
return MessageFormatter.format(VALUE_LT, fieldValue).getMessage();
}
return null;
}
}

View File

@ -1,282 +0,0 @@
package cn.iocoder.dashboard.framework.validator.custom.handler;
import cn.hutool.core.collection.CollectionUtil;
import io.swagger.annotations.ApiModelProperty;
import lombok.Setter;
import org.hibernate.validator.internal.engine.path.PathImpl;
import javax.validation.*;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.ValidateUnwrappedValue;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
/**
* 校验处理辅助类
*
* @author zzf
* @date 2021/4/15 9:49
*/
public class ValidateHandlerHelper {
private static final Set<ValidateAnnotationHandler<?>> handlerSet = new HashSet<>(10);
/**
* 初始化注册校验处理类
*/
static {
handlerSet.add(new NotNullHandler());
handlerSet.add(new NotEmptyHandler());
handlerSet.add(new NotBlankHandler());
handlerSet.add(new MaxHandler());
handlerSet.add(new MinHandler());
}
/**
* 校验指定字段值是否合法
* <p>
* 这里针对集合类型字段做递归校验
* 实现深层对象参数值校验
*
* @param field 字段对象
* @param paramObject 需要校验的字段所在的对象
* @param rootBean 需要校验的方法所在的类对象
* @param propertyPath 属性路径
* @param <T> 需要校验的方法所在的类
* @return 校验结果集
*/
public static <T> Set<ConstraintViolation<T>> validate(Field field,
Object paramObject,
T rootBean,
String propertyPath) {
Set<ConstraintViolation<T>> result = new HashSet<>();
Object fieldValue;
try {
field.setAccessible(true);
fieldValue = field.get(paramObject);
} catch (IllegalAccessException e) {
return result;
}
if (fieldValue instanceof Collection) {
Collection<?> collectionFieldValue = (Collection<?>) fieldValue;
if (CollectionUtil.isNotEmpty(collectionFieldValue)) {
for (Object collectionElement : collectionFieldValue) {
Field[] fields = collectionElement.getClass().getDeclaredFields();
for (Field collectionFieldElementField : fields) {
result.addAll(
validate(
collectionFieldElementField,
collectionElement,
rootBean,
propertyPath + "." + collectionFieldElementField.getName()
)
);
}
}
}
}
CustomConstraintViolation<T> violation = validateField(field, paramObject, rootBean, propertyPath, fieldValue);
if (violation != null) {
result.add(violation);
}
return result;
}
/**
* 校验字段值如果值合法返回null不合法返回消息内容
*
* @param field 需要校验的字段
* @param targetObject 字段所在的对象
* @return 值合法返回null不合法返回消息内容
*/
private static <T> CustomConstraintViolation<T> validateField(Field field,
Object targetObject,
T rootBean,
String propertyPath,
Object fieldValue) {
Optional<ValidateAnnotationHandler<?>> handlerOptional =
handlerSet.stream().filter(s -> s.isAnnotationPresent(field)).findFirst();
if (handlerOptional.isPresent()) {
String validate = handlerOptional.get().validate(field, targetObject);
if (validate != null) {
String fieldComment;
if (field.isAnnotationPresent(ApiModelProperty.class)) {
ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
fieldComment = annotation.value();
} else {
fieldComment = field.getName();
}
String msg = fieldComment + validate;
CustomConstraintDescriptor<?> customConstraintDescriptor =
new CustomConstraintDescriptor<>(field.getAnnotation(handlerOptional.get().getAnnotation()));
return CustomConstraintViolation.of(rootBean, msg, propertyPath, fieldValue, customConstraintDescriptor);
}
}
return null;
}
/**
* 校验不通过的返回值对象
*
* @param <T> 需要校验的方法所在的类
*/
public static class CustomConstraintViolation<T> implements ConstraintViolation<T> {
public static <T> CustomConstraintViolation<T> of(T rootBean,
String msg,
String path,
Object invalidValue,
ConstraintDescriptor<?> constraintDescriptor) {
CustomConstraintViolation<T> constraintViolation = new CustomConstraintViolation<>();
constraintViolation.setMessage(msg);
constraintViolation.setRootBean(rootBean);
constraintViolation.setPropertyPath(PathImpl.createPathFromString(path));
constraintViolation.setInvalidValue(invalidValue);
constraintViolation.setConstraintDescriptor(constraintDescriptor);
return constraintViolation;
}
@Setter
private String message;
@Setter
private T rootBean;
@Setter
private Path propertyPath;
@Setter
private Object invalidValue;
@Setter
private ConstraintDescriptor<?> constraintDescriptor;
@Override
public String getMessage() {
return message;
}
@Override
public String getMessageTemplate() {
return null;
}
@Override
public T getRootBean() {
return rootBean;
}
@Override
public Class<T> getRootBeanClass() {
return null;
}
@Override
public Object getLeafBean() {
return null;
}
@Override
public Object[] getExecutableParameters() {
return new Object[0];
}
@Override
public Object getExecutableReturnValue() {
return null;
}
@Override
public Path getPropertyPath() {
return propertyPath;
}
@Override
public Object getInvalidValue() {
return invalidValue;
}
@Override
public ConstraintDescriptor<?> getConstraintDescriptor() {
return constraintDescriptor;
}
@Override
public <U> U unwrap(Class<U> type) {
return null;
}
}
public static class CustomConstraintDescriptor<R extends Annotation> implements ConstraintDescriptor<R> {
private final R annotation;
public CustomConstraintDescriptor(R annotation) {
this.annotation = annotation;
}
@Override
public R getAnnotation() {
return annotation;
}
@Override
public String getMessageTemplate() {
return null;
}
@Override
public Set<Class<?>> getGroups() {
return null;
}
@Override
public Set<Class<? extends Payload>> getPayload() {
return null;
}
@Override
public ConstraintTarget getValidationAppliesTo() {
return null;
}
@Override
public List<Class<? extends ConstraintValidator<R, ?>>> getConstraintValidatorClasses() {
return null;
}
@Override
public Map<String, Object> getAttributes() {
return new HashMap<>();
}
@Override
public Set<ConstraintDescriptor<?>> getComposingConstraints() {
return null;
}
@Override
public boolean isReportAsSingleViolation() {
return false;
}
@Override
public ValidateUnwrappedValue getValueUnwrapping() {
return null;
}
@Override
public <U> U unwrap(Class<U> type) {
return null;
}
}
}

View File

@ -6,8 +6,8 @@ package cn.iocoder.dashboard.framework.validator.custom;
* 功能说明https://blog.csdn.net/qq_38688267/article/details/115720412
* <p>
* 环境搭建
* 1实现{@link cn.iocoder.dashboard.framework.validator.custom.handler.ValidateAnnotationHandler}
* 2将实现类注册到{@link cn.iocoder.dashboard.framework.validator.custom.handler.ValidateHandlerHelper}
* 1实现{@link cn.iocoder.dashboard.framework.validator.custom.ValidateAnnotationHandler}
* 2将实现类注册到{@link cn.iocoder.dashboard.framework.validator.custom.ValidateHandlerHelper}
* 3实现全局异常拦截{@link javax.validation.ConstraintViolationException}异常
*
* 使用步骤

View File

@ -0,0 +1,6 @@
cn.iocoder.dashboard.framework.validator.custom.handler.MaxHandler
cn.iocoder.dashboard.framework.validator.custom.handler.MinHandler
cn.iocoder.dashboard.framework.validator.custom.handler.NotBlankHandler
cn.iocoder.dashboard.framework.validator.custom.handler.NotEmptyHandler
cn.iocoder.dashboard.framework.validator.custom.handler.NotNullHandler
cn.iocoder.dashboard.framework.validator.custom.handler.SizeHandler