validation参数检验 - 注解介绍

文章目录

  • Maven 依赖
  • 注解介绍
    • javax.validation 中的注解(22个)
      • Null、NotNull
      • NotBlank
      • NotEmpty
      • Size
      • AssertFalse、AssertTrue
      • DecimalMax、DecimalMin、Max、Min
      • Digits
      • Positive、PositiveOrZero、Negative、NegativeOrZero
      • Future、FutureOrPresent、Past、PastOrPresent
      • Email、Pattern
    • hibernate.validator 中的注解
      • DurationMax、DurationMax
      • Length
      • Range
      • UniqueElements
  • 使用技巧
    • 自定义 message、读取配置文件
  • 问题
    • 为什么同是基本类型,boolean、int 对 null 的处理不同?

Maven 依赖

项目为 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>

注解介绍

javax.validation 中的注解(22个)

validation参数检验 - 注解介绍_第1张图片

注解 验证的数据类型 说明
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、NotNull

验证元素值必须为 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;
    }

validation参数检验 - 注解介绍_第2张图片

NotBlank

验证元素值不能为null,并且至少包含一个非空白字符。支持的类型:CharSequence

    @GetMapping("notBlank")
    public String notBlank(@NotBlank String b) {
        return "pass: " + b;
    }

validation参数检验 - 注解介绍_第3张图片

NotEmpty

验证元素值不能为null,且不能为空。支持的类型:

  • CharSequence (length of character sequence is evaluated)
  • Collection (collection size is evaluated)
  • Map (map size is evaluated)
  • Array (array length is evaluated)
    @GetMapping("notEmpty")
    public String notEmpty(@NotEmpty @RequestParam(name = "list", required = false) ArrayList<String> list) {
        return "pass: " + list;
    }

validation参数检验 - 注解介绍_第4张图片

Size

验证元素的size必须在 min 和 max 之间(包含边界),认为 null 是有效的。支持的类型:

  • CharSequence (length of character sequence is evaluated)
  • Collection (collection size is evaluated)
  • Map (map size is evaluated)
  • Array (array length is evaluated)

validation参数检验 - 注解介绍_第5张图片
min()
size 的最小值,默认为 0

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;
    }

validation参数检验 - 注解介绍_第6张图片

AssertFalse、AssertTrue

验证元素值必须为false/true,认为 null 是有效的(见测试结果),支持booleanBoolean

    @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;
    }

测试结果

validation参数检验 - 注解介绍_第7张图片

validation参数检验 - 注解介绍_第8张图片

validation参数检验 - 注解介绍_第9张图片

validation参数检验 - 注解介绍_第10张图片

注解 数据类型 true false null
@AssertTrue 基本类型 × ×
@AssertTrue 包装类型 ×
@AssertFalse 基本类型 ×
@AssertFalse 包装类型 ×

关于 null 的特除处理
  在AssertTrueAssertFalse的 javadoc 中都有一句 null elements are considered valid,意思就是 null 被认为是有效的。那为什么 AssertTrue 对基本类型 boolean 会验证失败呢?我觉得是因为基本类型的默认值导致的,当前端传值为 null 时,Boolean会使用直接使用 null,但是基本类型不能为 null,所以boolean使用的是默认值 false,false != true,所以验证失败。

DecimalMax、DecimalMin、Max、Min

验证元素值必须小于等于 / 大于等于指定的 value 值,认为 null 是有效的。

  • BigDecimal
  • BigInteger
  • CharSequence,Max、Min 不支持
  • byte、short、int、long 及其包装类型
  • 由于舍入错误,不支持double和float

validation参数检验 - 注解介绍_第11张图片

validation参数检验 - 注解介绍_第12张图片
value() 方法
指定的最大、最小值

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;
    }

validation参数检验 - 注解介绍_第13张图片
上图中使用的是包装类型 Integer, 有两个特殊情况:

  1. 当无法将字符串转换为对应数值类型时,会抛出异常,但此异常并不是验证失败,因为还没有走到验证那一步就报错了
  2. 当值为 null时,通过了验证

validation参数检验 - 注解介绍_第14张图片
上图中使用的是基本类型 int,与图 decimalMinInteger 相比, 可以看出与 Integer 的区别,在于对 null 的处理,int 是直接抛出了异常,而 Integer 是通过了验证

validation参数检验 - 注解介绍_第15张图片
上图中 inclusive = false,与图 decimalMinInteger 的第二个小图相比,图 decimalMinIntegerExclusive 没有通过验证。

Digits

验证元素整数位数的上限 integer 小数位数的上限 fraction,认为 null 是有效的。支持 BigDecimal、BigInteger、CharSequence,byte、 short、int、long 及其包装类型,由于舍入错误,不支持double和float

validation参数检验 - 注解介绍_第16张图片
integer()
整数位数的最大值

fraction()
小数位数的最大值

    @GetMapping("digits")
    public String digits(@Digits(integer = 2, fraction = 1) BigDecimal b) {
        return "pass: " + b;
    }

validation参数检验 - 注解介绍_第17张图片

Positive、PositiveOrZero、Negative、NegativeOrZero

验证元素必须为正数(Positive)、正数或 0 (PositiveOrZero)、负数(Negative)、负数或 0 (NegativeOrZero),认为 null 是有效的 ,支持的类型:

  • BigDecimal
  • BigInteger
  • byte、short、int、long、float、double 及其包装类型
    @GetMapping("positive")
    public String positive(@Positive Integer t) {
        return "pass: " + t;
    }

    @GetMapping("positiveOrZero")
    public String positiveOrZero(@PositiveOrZero Integer t) {
        return "pass: " + t;
    }

validation参数检验 - 注解介绍_第18张图片

validation参数检验 - 注解介绍_第19张图片

Future、FutureOrPresent、Past、PastOrPresent

验证元素值必须是一个将来的时间(Future)、将来的时间或当前时间(FutureOrPresent)、过去的时间(Past)、过去的时间或当前时间(PastOrPresent),认为 null 是有效的 。此处 present 的概念是相对于使用约束的类型定义的。例如,如果约束条件是一年,那么当前时间就意味着整个当年。支持的类型:

  • java.util.Date
  • java.util.Calendar
  • java.time.Instant
  • java.time.LocalDate
  • java.time.LocalDateTime
  • java.time.LocalTime
  • java.time.MonthDay
  • java.time.OffsetDateTime
  • java.time.OffsetTime
  • java.time.Year
  • java.time.YearMonth
  • java.time.ZonedDateTime
  • java.time.chrono.HijrahDate
  • java.time.chrono.JapaneseDate
  • java.time.chrono.MinguoDate
  • java.time.chrono.ThaiBuddhistDate
    @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

validation参数检验 - 注解介绍_第20张图片

validation参数检验 - 注解介绍_第21张图片
测试时间为 2020-04-30,可以看到图 future、图 futureOrPresent 的第二张小图,一个验证失败,一个验证通过。

Email、Pattern

验证注解的元素值是Email(Email)、符合正则表达式(Pattern),可以通过 regexp 和 flag 指定自定义的 email、正则格式,认为 null 是有效的 ,支持的类型:CharSequence。

validation参数检验 - 注解介绍_第22张图片
regexp()
正则表达式,Email 默认为 .* 任意字符,Pattern无默认值。
flags()
指定正则的匹配模式,不会用(手动狗头)。

    @GetMapping("email")
    public String email(@Email(regexp = ".+@.+") String email) {
        return "pass: " + email;
    }

validation参数检验 - 注解介绍_第23张图片

hibernate.validator 中的注解

validation参数检验 - 注解介绍_第24张图片

注解 数据类型 说明
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 验证波兰纳税人识别码

DurationMax、DurationMax

注解的 Duration 必须小于等于(DurationMax)、大于等于(DurationMin) 指定的值,默认包含边界。支持的类型 java.time.Duration

validation参数检验 - 注解介绍_第25张图片

@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();
    }
}

validation参数检验 - 注解介绍_第26张图片

Length

字符长度必须在 min 和 max之间,包含边界。支持的类型:CharSequence

validation参数检验 - 注解介绍_第27张图片

    @GetMapping("length")
    public String length(@Length(min = 2, max = 3) StringBuilder s) {
        return "pass: " + s.toString();
    }

validation参数检验 - 注解介绍_第28张图片

Range

数字大小必须在 min 和 max 之间,包含边界。支持的类型:

  • BigDecimal
  • BigInteger
  • CharSequence
  • byte、 short、int、long 及其包装类型

validation参数检验 - 注解介绍_第29张图片

    @GetMapping("range")
    public String range(@Range(min = 3, max = 10) Integer s) {
        return "pass: " + s.toString();
    }

validation参数检验 - 注解介绍_第30张图片

UniqueElements

检测集合中的值都是唯一的(集合不能包含相同的元素),null 是有效的。支持的类型:Collection

    @GetMapping("uniqueElements")
    public String uniqueElements(@UniqueElements @RequestParam("list") List<String> list) {
        return "pass: " + list;
    }

validation参数检验 - 注解介绍_第31张图片

使用技巧

自定义 message、读取配置文件

问题

为什么同是基本类型,boolean、int 对 null 的处理不同?

如下代码可以看出,在值为 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;
	}

你可能感兴趣的:(Spring)