springboot+validation校验参数


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 还支持:ReadablePartialReadableInstant 检查带注释的日期是否在未来
@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 还支持:ReadablePartialReadableInstant 检查带注释的日期是现在还是将来
@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 还支持:ReadablePartialReadableInstant 检查带注释的日期是否在过去
@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 还支持:ReadablePartialReadableInstant 检查带注释的日期是过去还是现在
@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和数组 检查注释元素的大小是否介于minmax(包括)之间
@Email CharSequence 检查指定的字符序列是否是有效的电子邮件地址。可选参数regexpflags允许指定电子邮件必须匹配的附加正则表达式(包括正则表达式标志)。
@Length CharSequence 验证带注释的字符序列是否介于minmax包含之间
@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(计算字符序列表示的数值)、任何子类型Numberjavax.money.MonetaryAmount 检查元素是否严格为正。零值被视为无效。
@PositiveOrZero BigDecimal、BigInteger、byte、short、int、long以及原始类型的相应包装器;HV 还支持:任何子类型CharSequence(计算字符序列表示的数值)、任何子类型Numberjavax.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 检查带注释的字符序列是否是有效的ISBNtype确定 ISBN 的类型。默认值为 ISBN-13。
@Length(min=, max=) CharSequence 验证带注释的字符序列是否介于minmax包含之间
@CodePointLength(min=, max=, normalizationStrategy=) CharSequence 验证带注释的字符序列的代码点长度是否介于两者之间minmax包括在内。如果设置,则验证标准化值normalizationStrategy
@LuhnCheck(startIndex= , endIndex=, checkDigitIndex=, ignoreNonDigitCharacters=) CharSequence 检查带注释的字符序列中的数字是否通过了 Luhn 校验和算法(另请参见Luhn 算法)。startIndexendIndex允许仅在指定的子字符串上运行算法。checkDigitIndex允许使用字符序列中的任意数字作为校验位。如果未指定,则假定校验位是指定范围的一部分。最后但同样重要的是,ignoreNonDigitCharacters允许忽略非数字字符。
@Mod10Check(multiplier=, weight=, startIndex=, endIndex=, checkDigitIndex=, ignoreNonDigitCharacters=) CharSequence 检查带注释的字符序列中的数字是否通过了通用 mod 10 校验和算法。multiplier确定奇数的乘数(默认为 3),weight偶数的权重(默认为 1)。startIndexendIndex允许仅在指定的子字符串上运行算法。checkDigitIndex允许使用字符序列中的任意数字作为校验位。如果未指定,则假定校验位是指定范围的一部分。最后但同样重要的是,ignoreNonDigitCharacters允许忽略非数字字符。
@Mod11Check(threshold=, startIndex=, endIndex=, checkDigitIndex=, ignoreNonDigitCharacters=, treatCheck10As=, treatCheck11As=) CharSequence 检查带注释的字符序列中的数字是否通过了 mod 11 校验和算法。threshold指定 mod11 乘数增长的阈值;如果未指定值,则乘数将无限增长。treatCheck10AstreatCheck11As指定当 mod 11 校验和分别等于 10 或 11 时要使用的校验位。分别默认为 X 和 0。startIndex,endIndex checkDigitIndexignoreNonDigitCharacters带有与 中相同的语义@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``RegexpURLValidatorconstraint-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


文章作者: Ming Ming Liu
文章链接: https://www.lmm.show/13/
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ming Ming Liu !
  目录