SpringMVC_数据校验

一、JSR 303

  • JSR 303 Java Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0
  • JSR 303 通过Bean 属性上标注类似于 @NotNull@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证

SpringMVC_数据校验_第1张图片


二、Hibernate Validator 扩展注解

Hibernate Validator JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解

 SpringMVC_数据校验_第2张图片

SpringMVC_数据校验_第3张图片

 SpringMVC_数据校验_第4张图片

 


三、Spring MVC 数据校验

  • Spring 4.0 拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。
  • Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。Spring MVC 中,可直接通过注解驱动的方式进行数据校验
  • Spring LocalValidatorFactroyBean 既实现了 Spring Validator 接口,也实现了 JSR 303 Validator 接口。只要在 Spring 容器中定义了一个 LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean 中。
  • Spring 本身并没有提供 JSR303 的实现,所以必须将 JSR303 的实现者的 jar 包放到类路径下
  • <mvc:annotation-driven/> 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @valid 注解即可让 Spring MVC 在完成数据绑定后执行数据校验的工作
  • 在已经标注了 JSR303 注解的表单/命令对象前标注一个 @ValidSpring MVC 框架在将请求参数绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验
  • Spring MVC 是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是 BindingResult Errors 类型,这两个类都位于 org.springframework.validation 包中
  • 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参
  • Errors 接口提供了获取错误信息的方法,如 getErrorCount() getFieldErrors(String field)
  • BindingResult 扩展了 Errors 接口

 SpringMVC_数据校验_第5张图片


四、在目标方法中获取校验结果

在表单/命令对象类的属性中标注校验注解,在处理方法对应的入参前添加 @ValidSpring MVC 就会实施校验并将校验结果保存在被校验入参对象之后的 BindingResult Errors 入参中。

常用方法:

  • FieldError getFieldError(String field)
  • List<FieldError> getFieldErrors()
  • Object getFieldValue(String field)
  • Int getErrorCount()

五、在页面上显示错误 

  • Spring MVC 除了会将表单/命令对象的校验结果保存到对应的 BindingResult Errors 对象中外,还会将所有校验结果保存到 隐含模型
  • 即使处理方法的签名中没有对应于表单/命令对象的结果入参,校验结果也会保存在 隐含对象中。
  • 隐含模型中的所有数据最终将通过 HttpServletRequest 的属性列表暴露给 JSP 视图对象,因此在 JSP 中可以获取错误信息
  • JSP 页面上可通过 <form:errors path=“userName”> 显示错误消息

六、示例

SpringMVC_数据校验_第6张图片

 

 SpringMVC_数据校验_第7张图片

 

 SpringMVC_数据校验_第8张图片


七、提示消息的国际化 

每个属性在数据绑定和数据校验发生错误时,都会生成一个对应的 FieldError 对象。

当一个属性校验失败后,校验框架会为该属性生成 4 个消息代码,这些代码以校验注解类名为前缀,结合 modleAttribute、属性名及属性类型名生成多个对应的消息代码:例如 User 类中的 password 属性标准了一个 @Pattern 注解,当该属性值不满足 @Pattern 所定义的规则时, 就会产生以下 4 个错误代码:

  • Pattern.user.password
  • Pattern.password
  • Pattern.java.lang.String
  • Pattern

当使用 Spring MVC 标签显示错误消息时, Spring MVC 会查看 WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认的错误消息,否则使用国际化消息。

若数据类型转换数据格式转换时发生错误,或该有的参数不存在,或调用处理方法时发生错误,都会在隐含模型中创建错误消息。其错误代码前缀说明如下:

  • required:必要的参数不存在。如 @RequiredParam(“param1”) 标注了一个入参,但是该参数不存在
  • typeMismatch:在数据绑定时,发生数据类型不匹配的问题
  • methodInvocationSpring MVC 在调用处理方法时发生了错误

注册国际化资源文件

 


八、代码演示

1. 输出错误消息

public class Employee {
    @NotEmpty(message = "姓名不能为空")
    @Length(min = 5, max = 17, message = "姓名长度为5到17位")
    private String lastName;

    @Email
    private String email;

    private Integer gender;

    /**
     * 规定页面提交的日期格式
     *
     * @Past:必须是一个过去的时间
     * @Future :必须是一个未来的时间
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @Past
    private Date birth;

注:需要自定义错误消息,字需要添加一个 message 属性即可,但是这样做我们就失去了国际化的功能

/**
 * 保存员工
 */
@RequestMapping(value = "/emp")
public String addEmp(@Valid Employee employee, BindingResult result,
                     Model model) {
    System.out.println("要添加的员工:" + employee);
    // 获取是否有校验错误
    boolean hasErrors = result.hasErrors();
    Map errorsMap = new HashMap<>();
    if (hasErrors) {
        List errors = result.getFieldErrors();
        for (FieldError fieldError : errors) {

            System.out.println("错误消息提示:" + fieldError.getDefaultMessage());
            System.out.println("错误的字段是?" + fieldError.getField());
            System.out.println(fieldError);
            System.out.println("------------------------");
            errorsMap.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
        //可以吧错误信息使用jsp进行回显
        model.addAttribute("errorInfo", errorsMap);
        System.out.println("有校验错误");
        return "index";
    } else {
        //...
        return "redirect:/success";
    }
}




 输出

要添加的员工:Employee{lastName='张三', email='666666qq.com', gender=0, birth=Thu Jul 31 00:00:00 CST 2025}
错误消息提示:不是一个合法的电子邮件地址
错误的字段是?email
Field error in object 'employee' on field 'email': rejected value [666666qq.com]; codes [Email.employee.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [employee.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@2fb720ec,org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@179c3b74]; default message [不是一个合法的电子邮件地址]
------------------------
错误消息提示:姓名长度为5到17位
错误的字段是?lastName
Field error in object 'employee' on field 'lastName': rejected value [张三]; codes [Length.employee.lastName,Length.lastName,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [employee.lastName,lastName]; arguments []; default message [lastName],17,5]; default message [姓名长度为5到17位]
------------------------
错误消息提示:需要是一个过去的时间
错误的字段是?birth
Field error in object 'employee' on field 'birth': rejected value [Thu Jul 31 00:00:00 CST 2025]; codes [Past.employee.birth,Past.birth,Past.java.util.Date,Past]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [employee.birth,birth]; arguments []; default message [birth]]; default message [需要是一个过去的时间]
------------------------
有校验错误


    org.hibernate
    hibernate-validator
    6.0.9.Final

2. 国际化演示

public class Employee {
    @NotEmpty
    @Length(min = 5, max = 17)
    private String lastName;

    @Email
    private String email;

    private Integer gender;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @Past
    private Date birth;

 注:如果注解已经有 message  属性了,那么这个字段将会失去国际化功能,每次输出的都是这个message信息

/**
 * 保存员工
 */
@RequestMapping(value = "/emp")
public String addEmp(@Valid Employee employee, BindingResult result,
                     Model model) {
    System.out.println("要添加的员工:" + employee);
    // 获取是否有校验错误
    boolean hasErrors = result.hasErrors();

    Map errorsMap = new HashMap();
    if (hasErrors) {
        List errors = result.getFieldErrors();
        for (FieldError fieldError : errors) {

            System.out.println("错误消息提示:" + fieldError.getDefaultMessage());
            System.out.println("错误的字段是?" + fieldError.getField());
            System.out.println(fieldError);
            System.out.println("------------------------");
            errorsMap.put(fieldError.getField(),
                    fieldError.getDefaultMessage());
        }
        model.addAttribute("errorInfo", errorsMap);
        System.out.println("有校验错误");
        return "index";
    } else {
       // employeeDao.save(employee);
        // 返回列表页面;重定向到查询所有员工的请求
        return "redirect:/emps";
    }
}

 

a)编写国际化配置文件

errors_en_US.properties

#精确优先
#Email.email=email incorrect!~~
Email=email buzhengque~~~
NotEmpty=must not empty~~
#占位符 动态传入
Length.java.lang.String=length incorrect ,must between {2} and {1} ~~
Past=must a past time~~~
typeMismatch.birth=birth geshi buzhengque

高级国际化 动态传入消息参数

{0}:永远都是当前属性名;
{1},{2}:参数的字典序。如下所示 {1} 代码的是 max 的值,{2} 代表的是 min 的值

@NotEmpty
@Length(min = 5, max = 17)
private String lastName;

 errors_zh_CN.properties

Email.email=邮箱不对!~~
NotEmpty=不能为空~~
Length.java.lang.String=长度不对~~
Past=时间必须是过去的~~~
typeMismatch.birth=生日的格式不正确

b)让SpringMVC管理国际化资源文件 




    
    




c)来到页面取值

${errorInfo.lastName}
${errorInfo.email}

${errorInfo.birth}

 

 

 

你可能感兴趣的:(SpringMVC)