目录
关于 bean 验证的规范官网
https://beanvalidation.org/
看官网目前只有 java 语言的,其他语言的还没有。
一般一个规范不局限于一种具体的语言。例如算法和设计模式,一个思想实现可以通过多种方式来实现。语言只是一种实现思想的工具。
在java中对应的规范是eclipse下的Jakarta Validation
https://github.com/jakartaee/validation
类似于 jdbc 规范,java 只是规定了连接数据库和crud的操作,但是具体各个数据库怎么来实现这些操作是各家数据库厂商的问题。也想起了电脑上的usb插口,什么设备都可以插还能识别正常运行。
maven 依赖
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
针对此规范开源社区中的 hibernate 进行了规范的实现
官网
https://hibernate.org/validator/
源码
https://github.com/hibernate/hibernate-validator
maven依赖
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.5.Final</version>
<scope>compile</scope>
</dependency>
通过 ResourceBundle 进行了国际化处理
通过内容可以看出,hibernate-validator 针对 规范做了扩充
javax.validation.constraints.AssertFalse.message = must be false
javax.validation.constraints.AssertTrue.message = must be true
javax.validation.constraints.DecimalMax.message = must be less than ${inclusive == true ? 'or equal to ' : ''}{value}
javax.validation.constraints.DecimalMin.message = must be greater than ${inclusive == true ? 'or equal to ' : ''}{value}
javax.validation.constraints.Digits.message = numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
javax.validation.constraints.Email.message = must be a well-formed email address
javax.validation.constraints.Future.message = must be a future date
javax.validation.constraints.FutureOrPresent.message = must be a date in the present or in the future
javax.validation.constraints.Max.message = must be less than or equal to {value}
javax.validation.constraints.Min.message = must be greater than or equal to {value}
javax.validation.constraints.Negative.message = must be less than 0
javax.validation.constraints.NegativeOrZero.message = must be less than or equal to 0
javax.validation.constraints.NotBlank.message = must not be blank
javax.validation.constraints.NotEmpty.message = must not be empty
javax.validation.constraints.NotNull.message = must not be null
javax.validation.constraints.Null.message = must be null
javax.validation.constraints.Past.message = must be a past date
javax.validation.constraints.PastOrPresent.message = must be a date in the past or in the present
javax.validation.constraints.Pattern.message = must match "{regexp}"
javax.validation.constraints.Positive.message = must be greater than 0
javax.validation.constraints.PositiveOrZero.message = must be greater than or equal to 0
javax.validation.constraints.Size.message = size must be between {min} and {max}
org.hibernate.validator.constraints.CreditCardNumber.message = invalid credit card number
org.hibernate.validator.constraints.Currency.message = invalid currency (must be one of {value})
org.hibernate.validator.constraints.EAN.message = invalid {type} barcode
org.hibernate.validator.constraints.Email.message = not a well-formed email address
org.hibernate.validator.constraints.ISBN.message = invalid ISBN
org.hibernate.validator.constraints.Length.message = length must be between {min} and {max}
org.hibernate.validator.constraints.CodePointLength.message = length must be between {min} and {max}
org.hibernate.validator.constraints.LuhnCheck.message = the check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed
org.hibernate.validator.constraints.Mod10Check.message = the check digit for ${validatedValue} is invalid, Modulo 10 checksum failed
org.hibernate.validator.constraints.Mod11Check.message = the check digit for ${validatedValue} is invalid, Modulo 11 checksum failed
org.hibernate.validator.constraints.ModCheck.message = the check digit for ${validatedValue} is invalid, {modType} checksum failed
org.hibernate.validator.constraints.Normalized.message = must be normalized
org.hibernate.validator.constraints.NotBlank.message = may not be empty
org.hibernate.validator.constraints.NotEmpty.message = may not be empty
org.hibernate.validator.constraints.ParametersScriptAssert.message = script expression "{script}" didn't evaluate to true
org.hibernate.validator.constraints.Range.message = must be between {min} and {max}
org.hibernate.validator.constraints.ScriptAssert.message = script expression "{script}" didn't evaluate to true
org.hibernate.validator.constraints.UniqueElements.message = must only contain unique elements
org.hibernate.validator.constraints.URL.message = must be a valid URL
org.hibernate.validator.constraints.br.CNPJ.message = invalid Brazilian corporate taxpayer registry number (CNPJ)
org.hibernate.validator.constraints.br.CPF.message = invalid Brazilian individual taxpayer registry number (CPF)
org.hibernate.validator.constraints.br.TituloEleitoral.message = invalid Brazilian Voter ID card number
org.hibernate.validator.constraints.pl.REGON.message = invalid Polish Taxpayer Identification Number (REGON)
org.hibernate.validator.constraints.pl.NIP.message = invalid VAT Identification Number (NIP)
org.hibernate.validator.constraints.pl.PESEL.message = invalid Polish National Identification Number (PESEL)
org.hibernate.validator.constraints.ru.INN.message = invalid Russian taxpayer identification number (INN)
org.hibernate.validator.constraints.time.DurationMax.message = must be shorter than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'}
org.hibernate.validator.constraints.time.DurationMin.message = must be longer than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'}
spring boot
在 spring boot 的 starter中有一个专门是关于验证的,正是用的 hibernate-validator。
这里有一个疑问:我所知的开源的关于验证规范的实现只有 hibernate 做了,所以用了这个?
在项目中要使用自动验证需要先做好配置
public Validator validator() {
return Validation.byProvider(HibernateValidator.class).configure().failFast(true).buildValidatorFactory().getValidator();
}
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(validator());
return processor;
}
然后在java代码中引入 javax.validation 下的相关注解
public class AdminLoginParam {
@NotEmpty
@ApiModelProperty(value = "用户名",required = true)
private String username;
@NotEmpty
@ApiModelProperty(value = "密码",required = true)
private String password;
}
在 controller 的方法中添加 @Validated 或者 @Valid 注解开启验证
public CommonResult<Admin> register(@Validated @RequestBody AdminParam adminParam) {
Admin admin = adminService.register(adminParam);
if (admin == null) {
return CommonResult.failed();
}
return CommonResult.success(admin);
}
关于验证的注解
javax.validation.Valid
org.springframework.validation.annotation.Validated
在 spring 的工具类 org.springframework.validation.annotation.ValidationAnnotationUtils
public abstract class ValidationAnnotationUtils {
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
/**
* Determine any validation hints by the given annotation.
* <p>This implementation checks for {@code @javax.validation.Valid},
* Spring's {@link org.springframework.validation.annotation.Validated},
* and custom annotations whose name starts with "Valid".
* @param ann the annotation (potentially a validation annotation)
* @return the validation hints to apply (possibly an empty array),
* or {@code null} if this annotation does not trigger any validation
*/
@Nullable
public static Object[] determineValidationHints(Annotation ann) {
Class<? extends Annotation> annotationType = ann.annotationType();
String annotationName = annotationType.getName();
if ("javax.validation.Valid".equals(annotationName)) {
return EMPTY_OBJECT_ARRAY;
}
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
if (validatedAnn != null) {
Object hints = validatedAnn.value();
return convertValidationHints(hints);
}
if (annotationType.getSimpleName().startsWith("Valid")) {
Object hints = AnnotationUtils.getValue(ann);
return convertValidationHints(hints);
}
return null;
}
private static Object[] convertValidationHints(@Nullable Object hints) {
if (hints == null) {
return EMPTY_OBJECT_ARRAY;
}
return (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints});
}
}
中 @Validated 或者 @Valid 注解按照同一个处理。
自定义验证注解
编写自定义注解
package com.cmft.common4es.config.validate;
import com.cmft.common4es.config.validate.validator.JsonValueValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* json参数格式校验注解
*/
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = JsonValueValidator.class)
@Documented
public @interface JsonValue {
String message() default "{com.cmft.common4es.config.validate.JsonValue.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
自定义注解处理
package com.cmft.common4es.config.validate.validator;
import com.cmft.common4es.config.validate.JsonValue;
import com.cmft.common4es.util.JsonUtils;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.invoke.MethodHandles;
/**
* json参数格式校验注解验证器
* @author Rike
* @date 2023/8/18
*/
public class JsonValueValidator implements ConstraintValidator<JsonValue, Object> {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
// logger.info("{}", value);
// logger.info("{}", context);
boolean flag = false;
try {
// 校验是否为json数据
ObjectNode objectNode = (ObjectNode) JsonUtils.getInstance().readTree(value.toString());
flag = true;
} catch (Exception e) {
logger.error("", e);
}
return flag;
}
@Override
public void initialize(JsonValue constraintAnnotation) {
logger.info("{}", MethodHandles.lookup().lookupClass());
}
}
使用注解的类
package com.cmft.common4es.entity;
import com.cmft.common4es.config.validate.ExecuteGroup;
import com.cmft.common4es.config.validate.JsonValue;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
/**
* 插入数据
*/
@Valid
public class InsertData extends BaseData {
/**
* 需要插入的数据
*/
@NotBlank(groups = {ExecuteGroup.First.class})
@JsonValue(groups = {ExecuteGroup.Second.class})
private String data;
/**
* 索引刷新策略
*
* 0 默认 不指定
* 1 等待刷新
* 2 强制刷新
*/
private String refreshPolicy = "0";
/**
* 主键策略
*
* 0 自动生成
* 1 指定
*/
private String primaryKeyPolicy = "0";
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public String getRefreshPolicy() {
return refreshPolicy;
}
public void setRefreshPolicy(String refreshPolicy) {
this.refreshPolicy = refreshPolicy;
}
public String getPrimaryKeyPolicy() {
return primaryKeyPolicy;
}
public void setPrimaryKeyPolicy(String primaryKeyPolicy) {
this.primaryKeyPolicy = primaryKeyPolicy;
}
}
在类上加 @Valid,字段上的注解上指定group
验证内容配置
ValidationMessages_zh_CN.properties
com.cmft.common4es.config.validate.RequestParameterValue.message=
com.cmft.common4es.config.validate.JsonValue.message=非json数据
com.cmft.common4es.config.validate.JsonArrayStringValue.message=非json数组数据
组合校验
验证组
package com.cmft.common4es.config.validate;
import javax.validation.GroupSequence;
@GroupSequence(value = {ExecuteGroup.First.class, ExecuteGroup.Second.class, ExecuteGroup.Third.class})
public @interface ExecuteGroup {
interface First {}
interface Second {}
interface Third {}
}
通过 @GroupSequence 指定验证顺序
在请求方法上使用 @Validated 指定验证组
@PostMapping(value = "insertBatchArrayStr")
public CommonResponse insertBatchArrayStr(@RequestBody @Validated(ExecuteGroup.class) InsertDataJsonArray insertData) {
String arrayStr = insertData.getJsonArrayStr();
boolean autoKey = StringUtils.equals(insertData.getPrimaryKeyPolicy(), "0");
ArrayNode node = (ArrayNode) JsonUtils.readTree(arrayStr);
List<JsonNode> nodeList = IteratorUtils.toList(node.iterator());
List<String> list = nodeList.parallelStream().map((JsonNode jsonNode) -> {
// 指定主键id
if (!autoKey) {
JsonNode temp = jsonNode.get("id");
if (Objects.isNull(temp)) {
throw new BusinessException("参数中指定了主键策略为自定义,但是参数jsonArrayStr中的数据未指定id");
}
}
return JsonUtils.toJsonString(jsonNode);
}).collect(Collectors.toList());
InsertDataBatch insertDataBatch = new InsertDataBatch();
BeanUtils.copyProperties(insertData, insertDataBatch);
insertDataBatch.setDataList(list);
insertService.insertBatch(insertDataBatch);
return CommonResponse.success();
}
类的属性上添加组合注解,类上添加 @Valid
代码如上
参考链接
https://blog.csdn.net/m0_63164811/article/details/126075344
https://blog.csdn.net/ww2651071028/article/details/130314309
https://blog.csdn.net/xxpxxpoo8/article/details/127551926
https://blog.csdn.net/weixin_43476020/article/details/128984428
文章出处登录后可见!