项目为 springboot 项目,主要依赖有以下三个,其中spring-boot-starter-validation包含了 jakarta.validation-api(包含了 javax.validation)、hibernate-validator
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
注解 | 验证的数据类型 | 说明 |
---|---|---|
Null | 所有类型 | 验证元素值必须为 null |
NotNull | 所有类型 | 验证元素值必须不为 null |
NotBlank | CharSequence | 验证元素值不能为 null,并且至少包含一个非空白字符。 |
NotEmpty | CharSequence、Collection、Map、Array | 验证元素值不能为 null,且不能为空 |
Size(min = min, max = max) | 同 NotEmpty | 验证元素的 size 必须在 min 和 max 之间(包含边界),认为 null 是有效的 |
AssertFalse | boolean、Boolean | 验证元素值必须为 false,认为 null 是有效的 |
AssertTrue | 同 AssertFalse | 验证元素值必须为 true,认为 null 是有效的 |
DecimalMax(value=, inclusive=) | BigDecimal、BigInteger、CharSequence,byte、 short、int、long 及其包装类型,由于舍入错误,不支持double和float | 验证元素值必须小于等于指定的 value 值,认为 null 是有效的 |
DecimalMin | 同 DecimalMax | 验证元素值必须大于等于指定的 value 值,认为 null 是有效的 |
Max | 同 DecimalMax,不支持CharSequence | 验证元素值必须小于等于指定的 value 值,认为 null 是有效的 |
Min | 同 DecimalMax,不支持CharSequence | 验证元素值必须大于等于指定的 value 值,认为 null 是有效的 |
Digits(integer =, fraction =) | 同 DecimalMax | 验证元素整数位数的上限 integer 与小数位数的上限 fraction,认为 null 是有效的 |
Positive | BigDecimal、BigInteger,byte、short、int、long、float、double 及其包装类型 | 验证元素必须为正数,认为 null 是有效的 |
PositiveOrZero | 同Positive | 验证元素必须为正数或 0,认为 null 是有效的 |
Negative | 同Positive | 验证元素必须为负数,认为 null 是有效的 |
NegativeOrZero | 同Positive | 验证元素必须为负数或 0,认为 null 是有效的 |
Future | Date、Calendar、Instant、LocalDate、LocalDateTime、LocalTime、MonthDay、OffsetDateTime、OffsetTime、Year、YearMonth、ZonedDateTime、HijrahDate、JapaneseDate、MinguoDate、ThaiBuddhistDate | 验证元素值必须是一个将来的时间,认为 null 是有效的 |
FutureOrPresent | 同 Future | 验证元素值必须是当前时间或一个将来的时间,认为 null 是有效的 |
Past | 同 Future | 验证元素值必须是一个过去的时间,认为 null 是有效的 |
PastOrPresent | 同 Future | 验证元素值必须是当前时间或一个过去的时间,认为 null 是有效的 |
Email(regexp = 正则表达式,flag = 标志的模式) | CharSequence | 验证注解的元素值是Email,可以通过 regexp 和 flag 指定自定义的 email 格式,认为 null 是有效的 |
Pattern | 同 Email | 验证元素值符合正则表达式,认为 null 是有效的 |
验证元素值必须为 null (Null),必须不为 null(NotNull),支持所有类型。
@GetMapping("null")
public String isNull(@Null Integer b) {
return "pass: " + b;
}
@GetMapping("notNull")
public String notNull(@NotNull Integer b) {
return "pass: " + b;
}
验证元素值不能为null,并且至少包含一个非空白字符。支持的类型:CharSequence
@GetMapping("notBlank")
public String notBlank(@NotBlank String b) {
return "pass: " + b;
}
验证元素值不能为null,且不能为空。支持的类型:
@GetMapping("notEmpty")
public String notEmpty(@NotEmpty @RequestParam(name = "list", required = false) ArrayList<String> list) {
return "pass: " + list;
}
验证元素的size必须在 min 和 max 之间(包含边界),认为 null 是有效的。支持的类型:
max()
size 的最大值,默认为 Integer.MAX_VALUE
@GetMapping("size")
public String size(@Size(min = 2, max = 3) @RequestParam(name = "list", required = false) ArrayList<String> list ) {
return "pass: " + list;
}
验证元素值必须为false/true,认为 null 是有效的(见测试结果),支持boolean
、Boolean
。
@GetMapping("wrapperAssertFalse")
public String wrapperAssertFalse(@AssertFalse Boolean b) {
return "pass: " + b;
}
@GetMapping("wrapperAssertTrue")
public String wrapperAssertTrue(@AssertTrue Boolean b) {
return "pass: " + b;
}
@GetMapping("primitiveAssertFalse")
public String primitiveAssertFalse(@AssertFalse boolean b) {
return "pass: " + b;
}
@GetMapping("primitiveAssertTrue")
public String primitiveAssertTrue(@AssertTrue boolean b) {
return "pass: " + b;
}
测试结果
注解 | 数据类型 | true | false | null |
---|---|---|---|---|
@AssertTrue | 基本类型 | √ | × | × |
@AssertTrue | 包装类型 | √ | × | √ |
@AssertFalse | 基本类型 | × | √ | √ |
@AssertFalse | 包装类型 | × | √ | √ |
关于 null 的特除处理
在AssertTrue
、AssertFalse
的 javadoc 中都有一句 null elements are considered valid,意思就是 null 被认为是有效的。那为什么 AssertTrue
对基本类型 boolean
会验证失败呢?我觉得是因为基本类型的默认值导致的,当前端传值为 null 时,Boolean
会使用直接使用 null,但是基本类型不能为 null,所以boolean
使用的是默认值 false,false != true,所以验证失败。
验证元素值必须小于等于 / 大于等于指定的 value 值,认为 null 是有效的。
inclusive() 方法
验证时是否可以包含边界值(value()
方法指定的值),默认为 true。
Max、Min 不支持。
@GetMapping("decimalMinInteger")
public String decimalMinInteger(@DecimalMin(value = "10") Integer b) {
return "pass: " + b;
}
@GetMapping("decimalMinInt")
public String decimalMinInt(@DecimalMin(value = "10") int b) {
return "pass: " + b;
}
@GetMapping("decimalMinIntegerExclusive")
public String decimalMinIntegerExclusive(@DecimalMin(value = "10", inclusive = false) Integer b) {
return "pass: " + b;
}
上图中使用的是基本类型 int
,与图 decimalMinInteger 相比, 可以看出与 Integer
的区别,在于对 null 的处理,int
是直接抛出了异常,而 Integer
是通过了验证
上图中 inclusive = false
,与图 decimalMinInteger 的第二个小图相比,图 decimalMinIntegerExclusive 没有通过验证。
验证元素整数位数的上限 integer 与小数位数的上限 fraction,认为 null 是有效的。支持 BigDecimal、BigInteger、CharSequence,byte、 short、int、long 及其包装类型,由于舍入错误,不支持double和float。
fraction()
小数位数的最大值
@GetMapping("digits")
public String digits(@Digits(integer = 2, fraction = 1) BigDecimal b) {
return "pass: " + b;
}
验证元素必须为正数(Positive)、正数或 0 (PositiveOrZero)、负数(Negative)、负数或 0 (NegativeOrZero),认为 null 是有效的 ,支持的类型:
@GetMapping("positive")
public String positive(@Positive Integer t) {
return "pass: " + t;
}
@GetMapping("positiveOrZero")
public String positiveOrZero(@PositiveOrZero Integer t) {
return "pass: " + t;
}
验证元素值必须是一个将来的时间(Future)、将来的时间或当前时间(FutureOrPresent)、过去的时间(Past)、过去的时间或当前时间(PastOrPresent),认为 null 是有效的 。此处 present 的概念是相对于使用约束的类型定义的。例如,如果约束条件是一年,那么当前时间就意味着整个当年。支持的类型:
@GetMapping("future")
public String future(@Future LocalDate t) {
return "pass: " + t;
}
@GetMapping("futureOrPresent")
public String futureOrPresent(@FutureOrPresent LocalDate t) {
return "pass: " + t;
}
测试时间为 2020-04-30
测试时间为 2020-04-30,可以看到图 future、图 futureOrPresent 的第二张小图,一个验证失败,一个验证通过。
验证注解的元素值是Email(Email)、符合正则表达式(Pattern),可以通过 regexp 和 flag 指定自定义的 email、正则格式,认为 null 是有效的 ,支持的类型:CharSequence。
regexp()
正则表达式,Email
默认为 .*
任意字符,Pattern
无默认值。
flags()
指定正则的匹配模式,不会用(手动狗头)。
@GetMapping("email")
public String email(@Email(regexp = ".+@.+") String email) {
return "pass: " + email;
}
注解 | 数据类型 | 说明 |
---|---|---|
CreditCardNumber(ignoreNonDigitCharacters=) | CharSequence | 检查字符序列是否通过Luhn校验和测试。请注意,此验证旨在检查用户错误,而不是信用卡有效性!ignoreNonDigitCharacters允许忽略非数字字符,默认值为false。 |
Currency | javax.money.MonetaryAmount 子类型 | 检查注解的货币单位 javax.money.MonetaryAmount 是否为指定货币单位的一部分 |
DurationMax | Duration | 注解的 Duration 必须小于等于 DurationMax 指定的值,默认包含边界 |
DurationMin | Duration | 注解的 Duration 必须大于等于 DurationMax 指定的值,默认包含边界 |
EAN | CharSequence | 是否是有效的国际商品编号,null 是有效的 |
ISBN | CharSequence | 是否是有效的国际标准书号,null 是有效的 |
Length | CharSequence | 长度必须在 min 和 max之间,包含边界 |
Range | BigDecimal、BigInteger、CharSequence,byte、 short、int、long 及其包装类型 | 数字大小必须在 min 和 max 之间,包含边界 |
UniqueElements | Collection | 检测集合中的值都是唯一的(集合不能包含相同的元素,null 是有效的) |
Url | CharSequence | 根据RFC2396检查字符序列是否为有效URL |
LuhnCheck | CharSequence | 检查字符序列中的数字是否通过Luhn校验和算法 |
Mod10Check | CharSequence | 检查字符序列中的数字是否通过通用mod 10校验和算法。 |
Mod11Check | CharSequence | 检查字符序列中的数字是否通过通用mod 11校验和算法。 |
ScriptAssert | 任意类型 | |
CodePointLength | CharSequence | |
CNPJ | CharSequence | 验证巴西公司纳税人注册编号 |
CPF | CharSequence | 验证巴西个人纳税人注册号 |
TituloEleitoral | CharSequence | 验证巴西选民身份证号 |
NIP | CharSequence | 验证波兰增值税标识号 |
PESEL | CharSequence | 验证波兰的国家标识号 |
REGON | CharSequence | 验证波兰纳税人识别码 |
注解的 Duration 必须小于等于(DurationMax)、大于等于(DurationMin) 指定的值,默认包含边界。支持的类型 java.time.Duration
。
@RestController
public class HibernateController {
private final HibernateService hibernateService;
public HibernateController(HibernateService hibernateService) {
this.hibernateService = hibernateService;
}
@GetMapping("durationMax")
public String durationMax(long minutes) {
Duration duration = Duration.ofMinutes(minutes);
return "pass: " + hibernateService.durationMax(duration);
}
}
@Validated
@Service
public class HibernateService {
public long durationMax(@DurationMax(minutes = 2) Duration d) {
return d.toMinutes();
}
}
字符长度必须在 min 和 max之间,包含边界。支持的类型:CharSequence
。
@GetMapping("length")
public String length(@Length(min = 2, max = 3) StringBuilder s) {
return "pass: " + s.toString();
}
数字大小必须在 min 和 max 之间,包含边界。支持的类型:
@GetMapping("range")
public String range(@Range(min = 3, max = 10) Integer s) {
return "pass: " + s.toString();
}
检测集合中的值都是唯一的(集合不能包含相同的元素),null 是有效的。支持的类型:Collection
。
@GetMapping("uniqueElements")
public String uniqueElements(@UniqueElements @RequestParam("list") List<String> list) {
return "pass: " + list;
}
如下代码可以看出,在值为 null 时,spring 对 Boolean 进行的特殊处理,直接返回了 false,而对其他基本类型抛出了异常。
// org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver
@Nullable
private Object handleNullValue(String name, @Nullable Object value, Class<?> paramType) {
if (value == null) {
if (Boolean.TYPE.equals(paramType)) {
return Boolean.FALSE;
}
else if (paramType.isPrimitive()) {
throw new IllegalStateException("Optional " + paramType.getSimpleName() + " parameter '" + name +
"' is present but cannot be translated into a null value due to being declared as a " +
"primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
}
}
return value;
}