Jakarta Bean 验证约束
注解 | 支持的数据类型 | 功能 |
---|---|---|
@AssertFalse | Boolean、boolean | 可以为null,如果不为null的话必须为false |
@AssertTrue | Boolean、boolean | 可以为null,如果不为null的话必须为true |
@DecimalMax(value=, inclusive=) | BigDecimal、 BigInteger、 CharSequence、 byte、 short、 int、long | inclusive 当=false时,检查注释值是否小于指定的最大值。否则该值是否小于或等于指定的最大值。参数值是根据字符串表示的最大值的BigDecimal 字符串表示。 |
@DecimalMin(value=, inclusive=) | BigDecimal、 BigInteger、 CharSequence、 byte、 short、 int、long | inclusive 当=false时,检查注释值是否大于指定的最小值。否则该值是否大于或等于指定的最小值。参数值是根据字符串表示的最小值的BigDecimal 字符串表示。 |
@Digits(integer=, fraction=) | BigDecimal、 BigInteger、 CharSequence、 byte、 short、 int、long | 检查带注释的值是否为最多integer 位数和fraction 小数位数的数字 |
@Future | 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; HV 还支持:ReadablePartial 和ReadableInstant |
检查带注释的日期是否在未来 |
@FutureOrPresent | 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; HV 还支持:ReadablePartial 和ReadableInstant |
检查带注释的日期是现在还是将来 |
@Past | 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; HV 还支持:ReadablePartial 和ReadableInstant |
检查带注释的日期是否在过去 |
@PastOrPresent | 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; HV 还支持:ReadablePartial 和ReadableInstant |
检查带注释的日期是过去还是现在 |
@Max(value=) | BigDecimal、 BigInteger、 byte、 short、 int、long | 检查注释值是否小于或等于指定的最大值 |
@Min(value=) | BigDecimal、 BigInteger、 byte、 short、 int、long | 检查注释值是否高于或等于指定的最小值 |
@NotNull | 任意类型 | 检查带注释的值是否不是null |
@Null | 任意类型 | 检查注释值是否为null |
@Pattern(regex=, flags=) | CharSequence | regex 检查带注释的字符串是否与考虑给定标志的正则表达式匹配match |
@Size(min=, max=) | CharSequence , Collection ,Map 和数组 |
检查注释元素的大小是否介于min 和max (包括)之间 |
CharSequence | 检查指定的字符序列是否是有效的电子邮件地址。可选参数regexp 和flags 允许指定电子邮件必须匹配的附加正则表达式(包括正则表达式标志)。 |
|
@Length | CharSequence | 验证带注释的字符序列是否介于min 和max 包含之间 |
@NotBlank | CharSequence | 检查带注释的字符序列不为空且修剪后的长度大于 0。不同之处@NotEmpty 在于,此约束只能应用于字符序列,并且忽略尾随空格。 |
@NotEmpty | CharSequence、 Collection、Map、数组 | 检查带注释的元素不为Null也不为空 |
@Range(min=, max=) | BigDecimal、 BigInteger、 CharSequence、 byte、 short、 int、long 以及原始类型的相应包装器 | 检查注释值是否介于(包括)指定的最小值和最大值之间 |
@Negative | BigDecimal、 BigInteger、 byte、 short、 int、long | 检查元素是否严格为负。零值被视为无效。 |
@NegativeOrZero | BigDecimal、 BigInteger、 byte、 short、 int、long | 检查元素是负数还是零。 |
@Positive | BigDecimal、BigInteger、byte、short、int、long以及原始类型的相应包装器;HV 还支持:任何子类型CharSequence (计算字符序列表示的数值)、任何子类型Number 和javax.money.MonetaryAmount |
检查元素是否严格为正。零值被视为无效。 |
@PositiveOrZero | BigDecimal、BigInteger、byte、short、int、long以及原始类型的相应包装器;HV 还支持:任何子类型CharSequence (计算字符序列表示的数值)、任何子类型Number 和javax.money.MonetaryAmount |
检查元素是正数还是零。 |
附加约束
除了 Jakarta Bean Validation API 定义的约束之外,Hibernate Validator 还提供了几个有用的自定义约束,如下所示。除了一个例外,这些约束也适用于字段/属性级别,只是@ScriptAssert类级别的约束。
注解 | 支持的数据类型 | 功能 |
---|---|---|
@CreditCardNumber(ignoreNonDigitCharacters=) | CharSequence | 检查带注释的字符序列是否通过了 Luhn 校验和测试。请注意,此验证旨在检查用户错误,而不是信用卡有效性!另请参阅信用卡号剖析。ignoreNonDigitCharacters 允许忽略非数字字符。默认值为false . |
@Currency(value=) | 的任何子类型javax.money.MonetaryAmount (如果JSR 354 API和实现在类路径上) |
检查注释的货币单位javax.money.MonetaryAmount 是否是指定货币单位的一部分。 |
@DurationMax(days=, hours=, minutes=, seconds=, millis=, nanos=, inclusive=) | java.time.Duration | 检查带注释java.time.Duration 的元素是否不大于根据注释参数构造的元素。inclusive 如果flag 设置为 ,则允许相等true 。 |
@DurationMin(days=, hours=, minutes=, seconds=, millis=, nanos=, inclusive=) | java.time.Duration | 检查带注释java.time.Duration 的元素是否不小于由注释参数构造的元素。inclusive 如果flag 设置为 ,则允许相等true 。 |
@EAN | CharSequence | 检查带注释的字符序列是否为有效的EAN条形码。type 确定条码的类型。默认值为 EAN-13。 |
@ISBN | CharSequence | 检查带注释的字符序列是否是有效的ISBN。type 确定 ISBN 的类型。默认值为 ISBN-13。 |
@Length(min=, max=) | CharSequence | 验证带注释的字符序列是否介于min 和max 包含之间 |
@CodePointLength(min=, max=, normalizationStrategy=) | CharSequence | 验证带注释的字符序列的代码点长度是否介于两者之间min 并max 包括在内。如果设置,则验证标准化值normalizationStrategy 。 |
@LuhnCheck(startIndex= , endIndex=, checkDigitIndex=, ignoreNonDigitCharacters=) | CharSequence | 检查带注释的字符序列中的数字是否通过了 Luhn 校验和算法(另请参见Luhn 算法)。startIndex 并endIndex 允许仅在指定的子字符串上运行算法。checkDigitIndex 允许使用字符序列中的任意数字作为校验位。如果未指定,则假定校验位是指定范围的一部分。最后但同样重要的是,ignoreNonDigitCharacters 允许忽略非数字字符。 |
@Mod10Check(multiplier=, weight=, startIndex=, endIndex=, checkDigitIndex=, ignoreNonDigitCharacters=) | CharSequence | 检查带注释的字符序列中的数字是否通过了通用 mod 10 校验和算法。multiplier 确定奇数的乘数(默认为 3),weight 偶数的权重(默认为 1)。startIndex 并endIndex 允许仅在指定的子字符串上运行算法。checkDigitIndex 允许使用字符序列中的任意数字作为校验位。如果未指定,则假定校验位是指定范围的一部分。最后但同样重要的是,ignoreNonDigitCharacters 允许忽略非数字字符。 |
@Mod11Check(threshold=, startIndex=, endIndex=, checkDigitIndex=, ignoreNonDigitCharacters=, treatCheck10As=, treatCheck11As=) | CharSequence | 检查带注释的字符序列中的数字是否通过了 mod 11 校验和算法。threshold 指定 mod11 乘数增长的阈值;如果未指定值,则乘数将无限增长。treatCheck10As 并treatCheck11As 指定当 mod 11 校验和分别等于 10 或 11 时要使用的校验位。分别默认为 X 和 0。startIndex ,endIndex checkDigitIndex 并ignoreNonDigitCharacters 带有与 中相同的语义@Mod10Check 。 |
@Normalized(form=) | CharSequence | 验证带注释的字符序列是否根据给定的form . |
@Range(min=, max=) | BigDecimal、 BigInteger、 CharSequence、 byte、 short、 int、long 以及原始类型的相应包装器 | 检查注释值是否介于(包括)指定的最小值和最大值之间 |
@ScriptAssert(lang=, script=, alias=, reportOn=) | 任意类型 | 检查是否可以根据注释元素成功评估给定脚本。为了使用此约束,由 JSR 223(“Java TM平台脚本”)定义的 Java 脚本 API 的实现必须是类路径的一部分。要评估的表达式可以用任何脚本或表达式语言编写,在类路径中可以找到 JSR 223 兼容引擎。即使这是一个类级别的约束,也可以使用该reportOn 属性来报告特定属性而不是整个对象的约束违规。 |
@UniqueElements | Collection | 检查带注释的集合是否仅包含唯一元素。使用该equals() 方法确定相等性。默认消息不包含重复元素的列表,但您可以通过覆盖消息并使用{duplicates} message 参数来包含它。重复元素列表也包含在约束违规的动态负载中。 |
@URL(protocol=, host=, port=, regexp=, flags=) | CharSequence | 根据 RFC2396 检查带注释的字符序列是否是有效的 URL。如果指定了任何可选参数或protocol ,则相应的 URL 片段必须与指定的值匹配。可选参数和允许指定 URL 必须匹配的附加正则表达式(包括正则表达式标志)。默认情况下,此约束使用构造函数来验证给定字符串是否表示有效 URL。基于正则表达式的版本也可用 - - 可以通过 XML(参见第 8.2 节,“通过映射约束”)或编程 API(参见第 12.15.2 节,“以编程方式添加约束定义”)进行配置。host``port``regexp``flags``java.net.URL``RegexpURLValidator constraint-mappings |
其他注解
注解 | 功能 | 示例 |
---|---|---|
@Valid | 用于校验,不提供分组功能 | public Girl addGirl(@Valid Girl girl) |
@Validated | @Validated是@Valid 的一次封装,是Spring提供的校验机制使用 | public Girl addGirl(@Validated Girl girl) |
自定义注解
StartsWith.java
package show.lmm.validation.core.annotation.validator.validation;
import show.lmm.validation.core.annotation.validator.StartWithValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
/**
* 字符串以prefix开头
*/
@Documented
@Target({PARAMETER, FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {StartWithValidator.class})
public @interface StartsWith {
/**
* 前缀
*/
String prefix();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String message() default "";
}
StartWithValidator.java
package show.lmm.validation.core.annotation.validator;
import show.lmm.validation.core.annotation.validator.validation.StartsWith;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class StartWithValidator implements ConstraintValidator<StartsWith, String> {
StartsWith param;
@Override
public void initialize(StartsWith constraintAnnotation) {
param = constraintAnnotation;
}
@Override
public boolean isValid(String baseValue, ConstraintValidatorContext context) {
return baseValue.startsWith(param.prefix());
}
}
配置全局异常
package show.lmm.validation;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.List;
import java.util.stream.Collectors;
/**
* 全局异常
*/
@RestControllerAdvice
public class GlobalExecption {
/**
* 参数校验异常
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public String handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
e.printStackTrace();
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
return allErrors.stream().map(s -> s.getDefaultMessage()).collect(Collectors.joining("\r\n"));
}
}
分组校验
问题:普通参数校验为通过时,不会进行分组校验
UserController.java
package show.lmm.validation.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import show.lmm.validation.core.view.request.user.AddEditUserInfoRequest;
/**
* 用户 controller
*/
@RestController
@RequestMapping("/user")
public class UserController {
/**
* 添加/修改用户信息
*
* @param info 用户信息
* @return
* @throws JsonProcessingException
*/
@PostMapping("/add_edit")
public String index(@RequestBody @Validated AddEditUserInfoRequest info) throws JsonProcessingException {
return new ObjectMapper().writeValueAsString(info);
}
}
AddEditUserInfoRequest.java
package show.lmm.validation.core.view.request.user;
import lombok.Data;
import org.hibernate.validator.group.GroupSequenceProvider;
import show.lmm.validation.core.annotation.validator.UserIdGroupSequenceProvider;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 用户信息
*/
@Data
@GroupSequenceProvider(UserIdGroupSequenceProvider.class)
public class AddEditUserInfoRequest {
public interface AddGroup{
}
public interface EditGroup{
}
private int userId;
@NotNull(message = "用户名不能为null")
@NotBlank(message = "用户名不能为空")
private String userName;
/**
* 性别:1、男,2、女
*/
@NotNull(message = "性别不能小于为null",groups = { AddGroup.class })
@Min(value = 1,message = "性别不能小于1",groups = { AddGroup.class })
@Max(value = 2,message = "性别不能大于2",groups = { AddGroup.class })
private Integer sex;
@NotNull(message = "个人简介不能为null",groups = { EditGroup.class })
@NotBlank(message = "个人简介不能为空",groups = { EditGroup.class })
private String profileInfo;
}
UserIdGroupSequenceProvider.java
package show.lmm.validation.core.annotation.validator;
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;
import show.lmm.validation.core.view.request.user.AddEditUserInfoRequest;
import java.util.ArrayList;
import java.util.List;
/**
* 用户分组序列提供者
*/
public class UserIdGroupSequenceProvider implements DefaultGroupSequenceProvider<AddEditUserInfoRequest> {
@Override
public List<Class<?>> getValidationGroups(AddEditUserInfoRequest bean) {
List<Class<?>> defaultGroupSequence = new ArrayList<>();
// 这一步不能省,否则Default分组都不会执行了,会抛错的
defaultGroupSequence.add(AddEditUserInfoRequest.class);
if (bean == null) {
return defaultGroupSequence;
}
if (bean.getUserId() < 1) {
defaultGroupSequence.add(AddEditUserInfoRequest.AddGroup.class);
} else {
defaultGroupSequence.add(AddEditUserInfoRequest.EditGroup.class);
}
return defaultGroupSequence;
}
}
示例
https://gitee.com/luoye/examples/tree/main/springbootValidation