初版提交
This commit is contained in:
parent
8a9c714586
commit
255aede037
|
@ -0,0 +1,135 @@
|
||||||
|
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;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.hibernate.validator.internal.util.Contracts;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
|
import javax.validation.Validator;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.executable.ExecutableValidator;
|
||||||
|
import javax.validation.metadata.BeanDescriptor;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Executable;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义参数校验器
|
||||||
|
* <p>
|
||||||
|
* 愿景:实现自动生成验证失败的消息
|
||||||
|
* 1、字段名根据{@link ApiModelProperty#value()}注解获取
|
||||||
|
* 2、状态根据{@link NotNull}、{@link javax.validation.constraints.NotEmpty}等生成
|
||||||
|
* 3、拼接返回消息
|
||||||
|
* <p>
|
||||||
|
* 如果实现后则以后只需要加{@code @NotNull @NotEmpty}注解,不需要再一个个写{@link NotNull#message()}的值
|
||||||
|
* <p>
|
||||||
|
* 2021.4.15 此次实现是类似于多数据源的实现,通过{@link ValidatorProxy}代理
|
||||||
|
* 默认校验类({@link org.hibernate.validator.internal.engine.ValidatorImpl})和自定义校验类(this)
|
||||||
|
* · 该类只处理参数校验{@link this#validateParameters(Object, Method, Object[], Class[])}
|
||||||
|
* · 其他校验都交给默认实现
|
||||||
|
* · 因此这个类只实现了参数校验的方法
|
||||||
|
*
|
||||||
|
* @author zzf
|
||||||
|
* @date 2021/4/8 14:09
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class CustomValidator implements Validator, ExecutableValidator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
|
||||||
|
return validate(object, null, new Object[]{object}, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BeanDescriptor getConstraintsForClass(Class<?> clazz) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Override
|
||||||
|
public <T> T unwrap(Class<T> type) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecutableValidator forExecutables() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateParameters(T object, Method method, Object[] parameterValues, Class<?>... groups) {
|
||||||
|
return validate(object, method, parameterValues, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateReturnValue(T object, Method method, Object returnValue, Class<?>... groups) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateConstructorParameters(Constructor<? extends T> constructor, Object[] parameterValues, Class<?>... groups) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateConstructorReturnValue(Constructor<? extends T> constructor, T createdObject, Class<?>... groups) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private <T> Set<ConstraintViolation<T>> validate(T object, Executable executable, Object[] parameterValues, Class<?>... groups) {
|
||||||
|
try {
|
||||||
|
log.warn("Object: {}, Executable: {}, parameterValues: {}, groups: {}",
|
||||||
|
JSON.toJSONString(object),
|
||||||
|
JSON.toJSONString(executable),
|
||||||
|
JSON.toJSONString(parameterValues),
|
||||||
|
JSON.toJSONString(groups)
|
||||||
|
);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<ConstraintViolation<T>> result = new HashSet<>();
|
||||||
|
sanityCheckGroups(groups);
|
||||||
|
if (ArrayUtil.isNotEmpty(parameterValues)) {
|
||||||
|
for (Object param : parameterValues) {
|
||||||
|
Field[] fields = param.getClass().getDeclaredFields();
|
||||||
|
for (Field field : fields) {
|
||||||
|
result.addAll(ValidateHandlerHelper.validate(field, param, object, field.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sanityCheckGroups(Class<?>[] groups) {
|
||||||
|
Contracts.assertNotNull(groups, MESSAGES.groupMustNotBeNull());
|
||||||
|
for (Class<?> clazz : groups) {
|
||||||
|
if (clazz == null) {
|
||||||
|
throw new IllegalArgumentException(MESSAGES.groupMustNotBeNull());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package cn.iocoder.dashboard.framework.validator.custom;
|
||||||
|
|
||||||
|
import org.springframework.boot.validation.MessageInterpolatorFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
|
||||||
|
|
||||||
|
import javax.validation.Validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数校验器配置类
|
||||||
|
*
|
||||||
|
* @author zzf
|
||||||
|
* @date 2021/4/13 14:13
|
||||||
|
*/
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
public class ValidateConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置一个校验器的代理类,自定义选择校验器进行校验
|
||||||
|
* <p>
|
||||||
|
* 这里必须加上@Primary注解,否则会跟{@link WebMvcConfigurationSupport#mvcValidator()}的冲突
|
||||||
|
*/
|
||||||
|
@Primary
|
||||||
|
@Bean
|
||||||
|
public Validator primaryValidator() {
|
||||||
|
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
|
||||||
|
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
|
||||||
|
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
|
||||||
|
CustomValidator customValidator = new CustomValidator();
|
||||||
|
factoryBean.afterPropertiesSet();
|
||||||
|
return new ValidatorProxy(factoryBean.getValidator(), customValidator);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package cn.iocoder.dashboard.framework.validator.custom;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
|
import javax.validation.Validator;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.executable.ExecutableValidator;
|
||||||
|
import javax.validation.metadata.BeanDescriptor;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义参数校验器
|
||||||
|
* <p>
|
||||||
|
* ###未完成###
|
||||||
|
* <p>
|
||||||
|
* 愿景:实现自动生成验证失败的消息
|
||||||
|
* 1、字段名根据{@link ApiModelProperty#value()}注解获取
|
||||||
|
* 2、状态根据{@link NotNull}、{@link javax.validation.constraints.NotEmpty}等生成
|
||||||
|
* 3、拼接返回消息
|
||||||
|
* <p>
|
||||||
|
* 如果实现后则以后只需要加{@code @NotNull @NotEmpty}注解,不需要再一个个写{@link NotNull#message()}的值
|
||||||
|
*
|
||||||
|
* @author zzf
|
||||||
|
* @date 2021/4/8 14:09
|
||||||
|
*/
|
||||||
|
public class ValidatorProxy implements Validator, ExecutableValidator {
|
||||||
|
|
||||||
|
private final Validator defaultValidator;
|
||||||
|
|
||||||
|
private final Validator customValidator;
|
||||||
|
|
||||||
|
public ValidatorProxy(Validator defaultValidator,
|
||||||
|
Validator customValidator) {
|
||||||
|
this.defaultValidator = defaultValidator;
|
||||||
|
this.customValidator = customValidator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
|
||||||
|
return customValidator.validate(object, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) {
|
||||||
|
return defaultValidator.validateProperty(object, propertyName, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) {
|
||||||
|
return defaultValidator.validateValue(beanType, propertyName, value, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BeanDescriptor getConstraintsForClass(Class<?> clazz) {
|
||||||
|
return defaultValidator.getConstraintsForClass(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Override
|
||||||
|
public <T> T unwrap(Class<T> type) {
|
||||||
|
return type.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecutableValidator forExecutables() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateParameters(T object, Method method, Object[] parameterValues, Class<?>... groups) {
|
||||||
|
return customValidator.forExecutables().validateParameters(object, method, parameterValues, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateReturnValue(T object, Method method, Object returnValue, Class<?>... groups) {
|
||||||
|
return defaultValidator.forExecutables().validateReturnValue(object, method, returnValue, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateConstructorParameters(Constructor<? extends T> constructor, Object[] parameterValues, Class<?>... groups) {
|
||||||
|
return defaultValidator.forExecutables().validateConstructorParameters(constructor, parameterValues, groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@Override
|
||||||
|
public <T> Set<ConstraintViolation<T>> validateConstructorReturnValue(Constructor<? extends T> constructor, T createdObject, Class<?>... groups) {
|
||||||
|
return defaultValidator.forExecutables().validateConstructorReturnValue(constructor, createdObject, groups);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package cn.iocoder.dashboard.framework.validator.custom.handler;
|
||||||
|
|
||||||
|
import org.slf4j.helpers.MessageFormatter;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Max;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大值校验处理类
|
||||||
|
*
|
||||||
|
* @author zzf
|
||||||
|
* @date 2021/4/15 9:21
|
||||||
|
*/
|
||||||
|
public class MaxHandler implements ValidateAnnotationHandler<Max> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Max> getAnnotation() {
|
||||||
|
return Max.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String validate(Max validateAnnotation, Object fieldValue) {
|
||||||
|
long value = validateAnnotation.value();
|
||||||
|
boolean valid;
|
||||||
|
if (fieldValue instanceof Integer) {
|
||||||
|
valid = value > (Integer) fieldValue;
|
||||||
|
} else if (fieldValue instanceof BigDecimal | fieldValue instanceof Double | fieldValue instanceof Short) {
|
||||||
|
valid = new BigDecimal(String.valueOf(value)).compareTo(new BigDecimal(String.valueOf(fieldValue))) > 0;
|
||||||
|
} else if (fieldValue instanceof Long) {
|
||||||
|
valid = value > (Long) fieldValue;
|
||||||
|
} else if (fieldValue instanceof Date) {
|
||||||
|
valid = value > ((Date) fieldValue).getTime();
|
||||||
|
} else {
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
if (!valid) {
|
||||||
|
MessageFormatter.format(getResultMsgWhenInvalid(), value).getMessage();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResultMsgWhenInvalid() {
|
||||||
|
return "的值必须小于{}!";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package cn.iocoder.dashboard.framework.validator.custom.handler;
|
||||||
|
|
||||||
|
import org.slf4j.helpers.MessageFormatter;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Min;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最小值校验处理类
|
||||||
|
*
|
||||||
|
* @author zzf
|
||||||
|
* @date 2021/4/15 9:21
|
||||||
|
*/
|
||||||
|
public class MinHandler implements ValidateAnnotationHandler<Min> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Min> getAnnotation() {
|
||||||
|
return Min.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String validate(Min validateAnnotation, Object fieldValue) {
|
||||||
|
long value = validateAnnotation.value();
|
||||||
|
boolean valid;
|
||||||
|
if (fieldValue instanceof Integer) {
|
||||||
|
valid = value < (Integer) fieldValue;
|
||||||
|
} else if (fieldValue instanceof BigDecimal | fieldValue instanceof Double | fieldValue instanceof Short) {
|
||||||
|
valid = new BigDecimal(String.valueOf(value)).compareTo(new BigDecimal(String.valueOf(fieldValue))) < 0;
|
||||||
|
} else if (fieldValue instanceof Long) {
|
||||||
|
valid = value < (Long) fieldValue;
|
||||||
|
} else if (fieldValue instanceof Date) {
|
||||||
|
valid = value < ((Date) fieldValue).getTime();
|
||||||
|
} else {
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
if (!valid) {
|
||||||
|
MessageFormatter.format(getResultMsgWhenInvalid(), value).getMessage();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResultMsgWhenInvalid() {
|
||||||
|
return "的值必须小于{}!";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package cn.iocoder.dashboard.framework.validator.custom.handler;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非空字符校验处理类
|
||||||
|
*
|
||||||
|
* @author zzf
|
||||||
|
* @date 2021/4/15 9:21
|
||||||
|
*/
|
||||||
|
public class NotBlankHandler implements ValidateAnnotationHandler<NotBlank> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<NotBlank> getAnnotation() {
|
||||||
|
return NotBlank.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String validate(NotBlank validateAnnotation, Object fieldValue) {
|
||||||
|
if(StrUtil.isBlankIfStr(fieldValue)) {
|
||||||
|
return getResultMsgWhenInvalid();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResultMsgWhenInvalid() {
|
||||||
|
return "格式错误或者没有有效字符!";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package cn.iocoder.dashboard.framework.validator.custom.handler;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容不可空校验处理类
|
||||||
|
*
|
||||||
|
* @author zzf
|
||||||
|
* @date 2021/4/15 9:21
|
||||||
|
*/
|
||||||
|
public class NotEmptyHandler implements ValidateAnnotationHandler<NotEmpty> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<NotEmpty> getAnnotation() {
|
||||||
|
return NotEmpty.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String validate(NotEmpty validateAnnotation, Object fieldValue) {
|
||||||
|
if(ObjectUtil.isEmpty(fieldValue)) {
|
||||||
|
return getResultMsgWhenInvalid();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResultMsgWhenInvalid() {
|
||||||
|
return "不能为空!!!!";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package cn.iocoder.dashboard.framework.validator.custom.handler;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 必传校验处理类
|
||||||
|
*
|
||||||
|
* @author zzf
|
||||||
|
* @date 2021/4/15 9:21
|
||||||
|
*/
|
||||||
|
public class NotNullHandler implements ValidateAnnotationHandler<NotNull> {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<NotNull> getAnnotation() {
|
||||||
|
return NotNull.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String validate(NotNull validateAnnotation, Object fieldValue) {
|
||||||
|
if (ObjectUtil.isNull(fieldValue)) {
|
||||||
|
return getResultMsgWhenInvalid();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResultMsgWhenInvalid() {
|
||||||
|
return "必传!";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package cn.iocoder.dashboard.framework.validator.custom.handler;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验注解处理抽象接口
|
||||||
|
* <p>
|
||||||
|
* ### 实现类要使用起来需要注册到{@link ValidateHandlerHelper}中 ###
|
||||||
|
*
|
||||||
|
* @author zzf
|
||||||
|
* @date 2021/4/15 9:22
|
||||||
|
*/
|
||||||
|
public interface ValidateAnnotationHandler<T extends Annotation> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取实现类的具体的注解类对象
|
||||||
|
*/
|
||||||
|
Class<T> getAnnotation();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断参数字段是否存在该注解
|
||||||
|
*
|
||||||
|
* @param field 字段对象
|
||||||
|
* @return 是否存在该注解
|
||||||
|
*/
|
||||||
|
default Boolean isAnnotationPresent(Field field) {
|
||||||
|
return field.isAnnotationPresent(getAnnotation());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验字段值,如果值合法返回null,不合法返回消息内容
|
||||||
|
*
|
||||||
|
* @param field 需要校验的字段
|
||||||
|
* @param targetObject 字段所在的对象
|
||||||
|
* @return 值合法返回null,不合法返回消息内容
|
||||||
|
*/
|
||||||
|
default String validate(Field field, Object targetObject) {
|
||||||
|
T annotation = field.getAnnotation(getAnnotation());
|
||||||
|
return validate(annotation, ReflectUtil.getFieldValue(targetObject, field));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验字段值,如果值合法返回null,不合法返回消息内容
|
||||||
|
* <p>
|
||||||
|
* 这里之所以没有直接返回boolean而是直接返回msg
|
||||||
|
* 是为了适配如{@link javax.validation.constraints.Max}{@link javax.validation.constraints.Min}等
|
||||||
|
* 消息中需要获取注解属性值的注解
|
||||||
|
*
|
||||||
|
* @param validateAnnotation 标记的注解
|
||||||
|
* @param fieldValue 字段值
|
||||||
|
* @return 如果值合法返回null,不合法返回消息内容
|
||||||
|
*/
|
||||||
|
String validate(T validateAnnotation, Object fieldValue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 消息模板
|
||||||
|
*/
|
||||||
|
String getResultMsgWhenInvalid();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,282 @@
|
||||||
|
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 + handlerOptional.get().getResultMsgWhenInvalid();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package cn.iocoder.dashboard.framework.validator.custom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数校验实现
|
||||||
|
* <p>
|
||||||
|
* 功能说明: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}中
|
||||||
|
* 3、实现全局异常拦截{@link javax.validation.ConstraintViolationException}异常
|
||||||
|
*
|
||||||
|
* 使用步骤:
|
||||||
|
* 1、给存在需要校验方法的Controller加上{@link javax.validation.Validator} 注解
|
||||||
|
* 2、给需要校验的方法的参数加上 {@link javax.validation.Valid} 注解
|
||||||
|
* 3、给需要校验的参数属性加上想要校验的注解,
|
||||||
|
* 如{@link javax.validation.constraints.NotNull} {@link javax.validation.constraints.NotEmpty}等
|
||||||
|
* <p>
|
||||||
|
* DEMO:
|
||||||
|
* {@link cn.iocoder.dashboard.modules.system.controller.auth.SysAuthController#login(cn.iocoder.dashboard.modules.system.controller.auth.vo.auth.SysAuthLoginReqVO)}
|
||||||
|
* <p>
|
||||||
|
* 亮点:
|
||||||
|
* 校验过程中有实现对集合类型对象的递归校验,实现深层校验
|
||||||
|
*/
|
|
@ -10,31 +10,32 @@ import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.Pattern;
|
import javax.validation.constraints.Pattern;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
@ApiModel("账号密码登陆 Request VO")
|
@ApiModel("账号密码登陆 Request VO")
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder
|
@Builder
|
||||||
public class SysAuthLoginReqVO {
|
public class SysAuthLoginReqVO implements Serializable {
|
||||||
|
|
||||||
@ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma")
|
@ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma")
|
||||||
@NotEmpty(message = "登陆账号不能为空")
|
@NotEmpty
|
||||||
@Length(min = 4, max = 16, message = "账号长度为 4-16 位")
|
@Length(min = 4, max = 16, message = "账号长度为 4-16 位")
|
||||||
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
|
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@ApiModelProperty(value = "密码", required = true, example = "buzhidao")
|
@ApiModelProperty(value = "密码", required = true, example = "buzhidao")
|
||||||
@NotEmpty(message = "密码不能为空")
|
@NotEmpty
|
||||||
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
|
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
@ApiModelProperty(value = "验证码", required = true, example = "1024")
|
@ApiModelProperty(value = "验证码", required = true, example = "1024")
|
||||||
@NotEmpty(message = "验证码不能为空")
|
@NotEmpty
|
||||||
private String code;
|
private String code;
|
||||||
|
|
||||||
@ApiModelProperty(value = "验证码的唯一标识", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
|
@ApiModelProperty(value = "验证码的唯一标识", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
|
||||||
@NotEmpty(message = "唯一标识不能为空")
|
@NotEmpty
|
||||||
private String uuid;
|
private String uuid;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue