SpringMVC(三十六):自定义参数校验(JSR303-BeanValidation)

star2017 1年前 ⋅ 347 阅读

系统提供对外接口或公用方法时,通常需要对入参校验,即直接在接口入口层就进行校验,不让非法参数流入到业务层导致异常,确保系统的健壮性。。

数据校验分客户端校验和服务端校验,客户端校验主要在页面通过JavaScript来实现,过滤正常用户的误操作,仅做初步过滤;服务端校验是整个应用阻止非法数据的最后防线,客户端校验绝不能替代服务端的校验,客户端校验可以降低服务器的负载。

Java EE 6 开始定义了一项为校验 Bean 数据合法性的规范 JSR-303,叫做 Bean Validation,该标准目标有两个实现:Hibernate ValidatorApache bval,使用较多的是前者。

Hibernate Validator (与 Hibernate ORM 没有关系)是 Bean Validation 的参考实现, 提供了 JSR-303 规范中所有内置 constraint 的实现,并扩展了一些常需要使用的 constraint,提供了一套比较完善,便捷的验证实现方式。

Validation

JSR-303

Bean Validation为 Java Bean 定义了相应的数据类型和 API,在应用中通过在Bean属性上标注类似于@NotNull, @Max等标准的注解指定校验规则,并通过验证接口对 Bean 进行验证。

该校验框架是一个运行时框架,在验证之后验证的错误信息被马上返回;核心接口是javax.validation.Validator,该接口根据目标对象类中所标注的校验注解进和地数据校验,并得到校验结果。

内置校验注解

JSR-303(Bean Validation)内置的 constraint(约束):

Constraint 描述 备注
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null 通常作用在对象属性上,
作用在 String 类型上无法校验空格
@NotEmpty 被注释的字符串的必须非空
@NotBlank 被注释的字符串必须不为 null,且必须至少包含一个非空白字符
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期或时间
@PastOrPresent 被注释的元素必须是当前或一个过去的日期或时间
@Future 被注释的元素必须是一个将来的日期或时间
@FutureOrPresent 被注释的元素必须是当前或一个将来的日期或时间
@Pattern(value) 被注释的元素必须符合指定的正则表达式
@Positive 被注释的元素必须是一个严格的正数 0 为非法值
@PositiveOrZero 被注释的元素必须是一个正数或为 0
@Negative 被注释的元素必须是一个严格的负数 0 为非法值
@NegativeOrZero 被注释的元素必须是一个负数或为 0
@Length 被注释的字符串的大小必须在指定的范围内
@Email 被注释的元素必须是电子邮箱地址
@Range 被注释的元素必须在合适的范围内

Hibernate Validator

Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。更多信息,请查看:Hibernate Validator - The Bean Validation reference implementation.Hibernate Validator 6.1.5.Final - Jakarta Bean Validation Reference Implementation: Reference Guide

扩展校验注解

Constraint 描述 备注
@Length 被注释的字符串的长度必须在 minmax 之间
@URL 被注释的字符串必须一个 URL
@Range 被注释的元素必须在合适的范围内 应用于数值或数值的字符串表示形式
@CreditCardNumber 被注释的字符串必须表示有效的信用卡号码 这是Luhn算法的实现,目的是检查用户的错误,
而不是信用卡的有效性!
@LuhnCheck Luhn 算法检查约束 允许验证一系列数字是否通过Luhn模10校验和算法,
Luhn Mod10的计算方法是将数字相加,
每个奇数位(从右到左)的值乘以2,如果值大于9,
则结果数字a在总和之前求和。
@Mod10Check 允许验证一系列数字是否通过Mod10校验和算法 经典的Mod10是通过将数字相加来计算的,
每一个奇数(从右到左)值乘以一个乘数 。
例如,ISBN-13是模10校验和(乘数是 3)。
@Mod11Check 允许验证一系列数字是否通过Mod11校验和算法。 对于最常见的Mod11变量,求和是通过将最右边的数字(不包括校验位)乘以最左边的一个权重来完成的。
权重从2开始,每位数增加1。然后使用(sum%11)计算校验位。
@ISBN 被注释的字符串必须是一个 ISBN ISBN:指国际标准书号,
专为识别图书等文南而设计的国际编号
@UniqueElements 验证所提供的 Collection中的每个对象是否唯一 即在集合中找不到2个相等的元素。
@ConstraintComposition 布尔合成运算约束 值为 CompositionType 枚举(OR,AND,ALL_FALSE)
@NotBlank 已弃用
@NotEmpty 已弃用
@Email 已弃用

校验返回策略

Hibernate Validator 提供了两种校验策略模式:

  • 普通模式:默认,会校验完所有的属性,然后返回所有的验证失败信息。
  • 快速失败返回模式:只要有一个验证失败,就立即返回失败信息。

配置方式:addProperty true 为快速失败返回模式,false 为普通模式。

@Configuration
public class ValidatorConfig {

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
        // 设置validator模式为快速失败返回模式
        postProcessor.setValidator(validator());
        return postProcessor;
    }

    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
            .configure()
            .addProperty(HibernateValidatorConfiguration.FAIL_FAST, "true")
            .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();
        return validator;
    }
}

手动使用 Validator 进行校验:

@Data // Lombok
public class User {
    @NotBlank(message = "姓名不能为空")
    private String name;
}

@ResponseBody
@GetMapping("/user")
public User user() {
    User user = new User();
    user.setName("");
    Set<ConstraintViolation<User>> violationSet = validator.validate(user);
    for (ConstraintViolation<User> item : violationSet) {
        logger.error("ErrorMessage:{}", item.getMessage());
    }
    return user;
}

Spring Validator

Spring 提供了自己的数据校验框架。Spring 在进行数据绑定时,可同时调用校验框架来完成数据校验的工作。

Spring 的校验框架在org.springframework.validation包中,重要的接口和类如下:

  • Validator:最重要的接口,里面有两个方法。

  • Errors:存放错误信息的接口。
    校验的结果必须是Errors或是BindingResult类型。

  • ValidatorUtils:校验的工具类,提供了多个Errors对象保存错误的方法。

  • LocalValidatorFactoryBean:该类实现了SpringValidator接口,也实现了JSR 303Validator接口,只要在 Spring 容器中定义一个 LocalValidatorFactoryBean,即可将其注入到需要数据校验的Bean中。

    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
    

    <mvc:annotation-driven/>会默认装配好一个LocalValidatorFactoryBean,实现开发中不需要手动配置。

Spring MVC 提供的数据校验是通过硬编码完成的,实现开发推荐使用JSR 303来进行数据校验。

Spring boot

如果是 Spring Boot 开发 Web 项目,spring-boot-starter-web 中已经含了 spring-boot-starter-validation包,所以不需要重复添加依赖。

如果没有用 Web 依赖想要使用 Validator 实现 Bean 验证,则添加 spring-boot-starter-validation 依赖即可,该依赖内部引入的是 hibernate-validator 实现校验。

spring-boot-starter-validation 包的依赖关系:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.2.6.RELEASE</version>
        <scope>compile</scope>
    </dependency>
    <!--Bean Validation, JSR 380-->
    <dependency>
        <groupId>jakarta.validation</groupId>
        <artifactId>jakarta.validation-api</artifactId>
        <version>2.0.2</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-el</artifactId>
        <version>9.0.33</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.0.18.Final</version>
        <scope>compile</scope>
        <exclusions>
            <exclusion>
                <artifactId>validation-api</artifactId>
                <groupId>javax.validation</groupId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

添加依赖

Spring Boot 项目添加 spring-boot-starter-validation 依赖,该依赖使用 Hibernate Validator 对 Java Bean 进行校验操作。

<!--校验 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Java Bean

@Data // Lombok
public class User {

    @NotBlank(message = "姓名不能为空")
    private String name;

    @NotBlank(message = "年龄不能为空")
    @Range(min = 18, max = 120, message = "年龄只能从18-120岁")
    private String age;

    // 日期正则校验
    @Pattern(regexp = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$", message = "出生日期格式不正确")
    private String birthday;

}

开启校验

在 Java Bean 类上 或 属性上添加验证注解,还需要在 Spring MVC Controller 方法入参使用 @Validated@Valid 注解开启校验。

  • @Valid:javax.validation 提供,作用在方法,构造方法,参数,成员属性上。

    @Valid 可做嵌套校验,作用在属性上,属性是一个 Java Bean,需要对属性 Bean 的内部属性进行校验。

    @Data // Lombok
    public class Father {
        @NotBlank(message = "name不能为空")
        private String name;
        @Valid
        private List<Son> sonList; 
    }
    
    @Data
    public class Son {
        @NotNull(message = "age不能为null")
        private Integer age;
    }
    
  • @Validated:Spring 提供(扩展了 @Valid),作用在类,方法,参数上。

    @Validated 支持分组,在接口形参上加上 @Validated({A.class}),只对该分组有效。例如,(@NotBlank(group={A.class},message = "name不能为空")

    注:A.class 可以是个空类,仅仅用于分组标识。

入参是 Java Bean

接口入参是 Java Bean 方式,@Valid 和 @Validated 都能起效。

@PostMapping("/user/query")
public ResponseModel<?> query(@RequestBody @Validated User user) throws Exception {
    return "省略";
}

入参非 Java Bean

此方式在参数前使用 @Valid@Validated 都不能起效,只能使用 @Validated 并注释在此方法所在的 Controller 类上才能起效。

@Validated
@RestController
@RequestMapping("/user")
public class UserController{

    @PostMapping("/query")
    public ResponseModel<?> query(@RequestBody @NotBlank(message = "name不能为空") String name) throws Exception {
        return "省略";
    }

    @GetMapping(value = "/get")
    public ResponseModel<?> getUser(
        @Min(value = 18, message = "age最小只能18") 
        @Max(value = 120, message = "age最大只能120")
        @RequestParam(name = "age", required = true) Integer age) {

        return age;
    }
}

此方式验证抛出的异常为:ConstraintViolationException,可使用全局异常统一处理。见下面 异常统一处理 章节。

验证失败处理

返回验证失败时的错误信息(例如,通过统一异常处理捕获验证异常,取出验证的错误信息)。

绑定验证结果

在验证入参的方法中,可绑定入参校验的结果对象,判断结果对象是否存在错误信息。

@PostMapping("/user/query")
public ResponseModel<?> query(@RequestBody @Valid User user, BindingResult result) throws Exception {
    if (result.hasErrors()) {
        // 校验失败,返回失败
        List<FieldError> errors = result.getFieldErrors();
        Map<String, Object> map = new HashMap<String, Object>();
        for (FieldError error : errors) {
            logger.warn("Field:{}, Message:{}", error.getField(), error.getDefaultMessage())
            map.put(error.getField(), error.getDefaultMessage());
        }
        return ResponseModel.fail(map);
    } 
    return "......省略....."
}

异常统一处理

Spring Boot 使用 @ControllerAdvice 进行统一异常处理,Spring MVC 框架抛出方法参数校验异常:MethodArgumentNotValidException,取出异常中的错误信息返回给客户端。

全局异常统一处理参考:Spring Boot 2实践系列(三十八):全局异常统一处理

if (e instanceof MethodArgumentNotValidException) {
    // 参数校验异常
    BindingResult result = ((MethodArgumentNotValidException) e).getBindingResult();
    if (result.hasErrors()) {
        List<ObjectError> errorList = result.getAllErrors();
        for (ObjectError error : errorList) {
            return ResponseModel.methodArgumentNotValid(error.getDefaultMessage());
        }
    }
}

// 或 
@ResponseBody
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseModel<?> methodArgValidationError(MethodArgumentNotValidException e) {
    BindingResult result = e.getBindingResult();
    final List<FieldError> fieldErrors = result.getFieldErrors();
    StringBuilder sb = new StringBuilder();
    for (FieldError error : fieldErrors) {
        error.getField(), error.getDefaultMessage()
        sb.append("Field:" + error.getField()).append(",").append("Message:" + error.getDefaultMessage());
    }
    logger.warn(sb.toString());
    return ResponseModel.fail(sb.toString());
}

/**
 * 处理 @Validated 作用在 Controller 类上, 方法参数使用校验注解
 */
@ResponseBody
@ExceptionHandler(ConstraintViolationException.class)
public ResponseModel<?> validationError(ConstraintViolationException e) {
    Set<ConstraintViolation<?>> violationSet = e.getConstraintViolations();
    StringBuilder sb = new StringBuilder();
    for (ConstraintViolation<?> item : violationSet) {
        sb.append( "ErrorMessage" + item.getMessage() + "\n");
    }
    logger.erwarnror(sb.toString());
    return ResponseModel.fail(sb.toString());
}

Spring 默认提供了一个配置校验器工厂 Bean,具体参考 SpringMVC-Validation数据校验

自定义校验注解

框架提供的校验注解(constraint)可能并不能完全满足需求, 所以需要自定义校验规则。例如对 IP 地址进行校验,对密码复杂度进行校验等。

Java 的注解使用,可参考 Java基础:Java 注解(Annotation)及使用

校验注解由两部分组成:

  • 校验注解:就一个普通的 Java 注解,但多个了元注解 @Constraint 用于指定该注解的 校验器。见下面章节的示例。

    @Constraint(validatedBy = {xxxxx.class})
    

    validatedBy 的值指定为校验逻辑的实现类,即校验器。

    自定义校验注解必须包含 message,groups,payload 属性。

  • 校验器:对校验注解进行解析,实现 ConstraintValidator接口,接口使用了泛型,需要指定两个参数,第一个是自定义注解类,第二个是需要校验的数据类型,重写 isValid(T value, ConstraintValidatorContext context)方法。

    public interface ConstraintValidator<A extends Annotation, T> {
    
        /**
         * 在调用 isValid 方法之前初始化
         * 该方法的参数类型为自定义注解,可调用注解的方法获取注解信息
         * 重写该方法, 给自定义校验器中定义的类变量赋值
         */
        default void initialize(A constraintAnnotation) {
        }
    
        /**
         * 重写该方法,实现校验逻辑,返回 布尔值
         */
        boolean isValid(T value, ConstraintValidatorContext context);
    }
    

    自定义注解校验器类实现了 ConstraintValidator 接口,默认会被 Spring 注册成 bean,所以可以在这个校验器类里面使用@Autowired 或者 @Resource 注入别的服务,不需要在类上添加 @Component 注解声明为 Spring 的 bean。

备注:自定义注解只能作用在接口 或 Controller 方法上才能启效,若写在实现类上是不启效的,只支持 Java Bean 验证。

自定义校验示例

金额精确2位小数

校验注解

/**
 * @desc: 金额数字校验
 */
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {AmountNumValidator.class})
public @interface AmountNum {

    String message() default "金额数字只精确到两位小数";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

注解解析器

/**
 * @desc: 金额数字校验
 */
public class AmountNumValidator implements ConstraintValidator<AmountNum, BigDecimal> {

    // 判断小数点后2位的数字的正则表达式
    Pattern pattern = Pattern.compile("^(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){0,2})?$");

    @Override
    public boolean isValid(BigDecimal amount, ConstraintValidatorContext context) {
        if (Objects.isNull(amount)) {
            return true;
        }
        Matcher match = pattern.matcher(amount.toString());
        return match.matches();
    }
}

保留小数位校验

校验注解

/**
 * @desc 小数最大位数限制
 */
@Documented
@Target({ FIELD, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Repeatable(List.class)
@Constraint(validatedBy = {DecimalPlaceMaxValidtor.class})
public @interface DecimalPlaceMax {

    int places();

    String message() default "";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /**
     * Defines several {@link DecimalPlaceMax} annotations on the same element.
     */
    @Target({ FIELD, CONSTRUCTOR, PARAMETER })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        DecimalPlaceMax[] value();
    }
}

注解解析器

/**
 * @desc 最大小数位数校验器
 */
public class DecimalPlaceMaxValidtor implements ConstraintValidator<DecimalPlaceMax, Number> {

    int places; // 最大小数位数

    @Override
    public void initialize(DecimalPlaceMax constraintAnnotation) {
        this.places = constraintAnnotation.places();
    }

    @Override
    public boolean isValid(Number value, ConstraintValidatorContext context) {
        if (Objects.isNull(value)) {
            return true;
        }
        String valueStr = String.valueOf(value);
        // Symbol.POINT = "."
        if (StringUtil.indexOf(valueStr, Symbol.POINT) < 0) {
            return true;
        }
        return StringUtil.split(valueStr, Symbol.POINT)[1].length() <= places;
    }
}

字符串是否在数组中

校验注解

/**
 * @desc 字符串是否属于定义的字符串数组中一个判断
 */
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { NotInStringsValidtor.class })
@Documented
public @interface NotInStrings {
    String message() default "请输入正确参数";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    String[] values() default {};
}

注解解析器

import org.apache.commons.lang3.StringUtils;

/**
 * @desc 字符串是否属于字符串数组中一个判断
 */
public class NotInStringsValidtor implements ConstraintValidator<NotInStrings, String> {

    String[] values; // 字符串数组

    @Override
    public void initialize(NotInStrings constraintAnnotation) {
        this.values = constraintAnnotation.values();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return StringUtils.equalsAny(value, values);
    }
}

字符串是否在枚举中

校验注解

/**
 * @desc 字符串是否属于枚举类型中一个判断
 */
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { NotEnumIncludeValidtor.class })
@Documented
public @interface NotEnumInclude {
    String message() default "";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    Class<?>[] target() default {};

    String methodName() default "name";
}

注解解析器

import org.apache.commons.lang3.StringUtils;
/**
 * @desc 字符串是否属于枚举类型中一个判断
 */
@Slf4j
public class NotEnumIncludeValidtor implements ConstraintValidator<NotEnumInclude, String> {

    Class<?>[] cls; // 枚举类
    String methodName;

    @Override
    public void initialize(NotEnumInclude constraintAnnotation) {
        cls = constraintAnnotation.target();
        this.methodName = constraintAnnotation.methodName();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (StringUtils.isBlank(value)) {
            return true;
        }
        if (cls.length > 0) {
            for (Class<?> cl : cls) {
                try {
                    if (cl.isEnum()) {
                        // 枚举类验证
                        Object[] objs = cl.getEnumConstants();
                        Method method = cl.getMethod(methodName);
                        for (Object obj : objs) {
                            Object code = method.invoke(obj);
                            if (value.equalsIgnoreCase(code.toString())) {
                                return true;
                            }
                        }
                    }
                } catch (Exception ex) {
                    log.error("catch an error when validate enum include", ex);
                }
            }
        } else {
            return true;
        }
        return false;
    }
}

数字是否在枚举中

校验注解

@Target({ElementType.FIELD, ElementType.PARAMETER})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Constraint(validatedBy = IntegerEnumValidator.class)  
public @interface IntegerEnum {  

    String message() default "invalid number";  

    int[] values() default {};  

    Class<?>[] groups() default {};  

    Class<? extends Payload>[] payload() default {};  

}

注解解析器

public class IntegerEnumValidator implements ConstraintValidator<IntegerEnum, Integer> {

    private IntegerEnum integerEnum;

    @Override
    public void initialize(IntegerEnum constraintAnnotation) {
        this.integerEnum = constraintAnnotation;
    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }
        int[] values = integerEnum.values();
        if (values.length == 0) {
            return true;
        }
        for (int v : values) {
            if (value == v) {
                return true;
            }
        }
        return false;
    }
}

注解使用

在实例类的属性上使用 @IntegerEnum 注解,指定枚举值。Controller 方法的入参增加 @Validated 注解使校验起效。

@Data  
public class User {  
    private Long id;  
    @IntegerEnum(values = {1, 2, 3}, message = "类型错误")  
    private Integer type;  
}

校验使用示例

  1. pom.xml文件引入Hibernate Validator jar包和依赖包。

    <!-- JSR 303数据校验实现:hibernate-validator -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.0.7.Final</version>
    </dependency>
    <!-- javax.el -->
    <dependency>
        <groupId>javax.el</groupId>
        <artifactId>javax.el-api</artifactId>
        <version>2.2.5</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.web</groupId>
        <artifactId>javax.el</artifactId>
        <version>2.2.5</version>
    </dependency>
    
  2. 实体类

    public class People {
    
        private String name;
        @NotBlank(message="登录名不能为空")
        private String loginName;
        @Length(min=6,max=12,message="密码长度必须在6位到8位之前")
        private String password;
        @NotBlank(message="用户名不能为空")
        private String userName;
        @Range(min=15,max=60,message="年龄必须在15到60岁之间")
        private double age;
        @Email(message="请输入合法邮箱地址")
        @NotBlank(message="邮箱地址不能为空")
        private String email;
        @DateTimeFormat(pattern="yyyy-MM-dd")
        @Past(message="生日必须是一个过去的日期")
        private Date birthDay;
        @Pattern(regexp="[1][3,8][3,6,9][0-9] {8}",message="无效电话号码")
        private String phone;
    
        //-------set/get方法------------
  3. Controller代码
    方法中接收参数的对象添加@Valid注解

    @Controller
    @RequestMapping("/user")
    public class LoginController {
    
        @RequestMapping(value = "/registerForm")
        public String registerForm() {
            return "registerForm";
        }
    
        @RequestMapping(value="/login", method=RequestMethod.POST)
        public String login(@Valid People people, Errors errors, Model model) {
            System.err.println(people);
            System.err.println(errors);
            if(errors.hasErrors()) {
                String defaultMessage = errors.getFieldError("age").getDefaultMessage();
                System.out.println(defaultMessage);
                return "registerForm";
            }
            model.addAttribute("people", people);
            return "success";
        }
    }
    
  4. JSP表单代码

    <%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    
    <script type="text/javascript"
        src="${pageContext.request.contextPath}/static/js/jquery-1.11.0.js"></script>
    
    <title>JSR 303 Validation</title>
    </head>
    <body>
        <h3>注册页面</h3>
        <form action="${pageContext.request.contextPath}/user/login"
            method="post">
            <table>
                <tr>
                    <td>登录名:</td>
                    <td><input name="loginName"></td>
                </tr>
                <tr>
                    <td>密码:</td>
                    <td><input name="password"></td>
                </tr>
                <tr>
                    <td>用户名:</td>
                    <td><input name="userName"></td>
                </tr>
                <tr>
                    <td>年龄:</td>
                    <td><input name="age"></td>
                </tr>
                <tr>
                    <td>邮箱:</td>
                    <td><input name="email"></td>
                </tr>
                <tr>
                    <td>生日:</td>
                    <td><input name="birthDay"></td>
                </tr>
                <tr>
                    <td>电话:</td>
                    <td><input name="phone"></td>
                </tr>
                <tr>
                    <td><input type="submit" value="提交"></td>
                </tr>
            </table>
    
        </form>
    </body>
    </html>
    

相关参考

  1. JSR 303 - Bean Validation 介绍及最佳实践
  2. Hibernate Validator:Bean Validation 的参考实现
  3. Hibernate Validator 6.1.5.Final - Jakarta Bean Validation Reference Implementation: Reference Guide
  4. JSR 303: Bean Validation
  5. JSR 380: Bean Validation 2.0
  6. Jakarta Bean Validation
  7. Jakarta-bean-validation 校验示例
  8. Validator 自动化校验
  9. Spring/Spring boot JSR-303验证框架 之 hibernate-validator
  10. Hibernate Validator 使用介绍
  11. 参数校验工具之Validator自定义校验
更多内容请访问:IT源点

相关文章推荐

全部评论: 0

    我有话说: