SpringMVC 17. 数据校验(JSR 303,Hibernate Validator 扩展注解,消息的国际化)

数据校验

JSR 303

  • JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 中 .

  • JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max等标准的注解指定校验规则,并通过标准的验证接口对 Bean进行验证

JSR303

Hibernate Validator 扩展注解

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

Hibernate Validator

Spring MVC 数据校验

  • Spring 4.0 拥有自己独立的数据校验框架,同时支持 JSR303 标准的校验框架。

  • 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 注解的表单/命令对象前标注一个@Valid,Spring MVC 框架在将请求参数绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验

  • Spring MVC 是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是 BindingResult 或Errors 类型,这两个类都位于org.springframework.validation 包中。

  • 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参

  • Errors 接口提供了获取错误信息的方法,如 getErrorCount() 或getFieldErrors(String field)

  • BindingResult 扩展了 Errors 接口
    BindingResult

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

  • 在表单/命令对象类的属性中标注校验注解,在处理方法对应的入参前添加 @Valid,Spring 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”>显示错误消息

示例代码:

  • 首先需要导入Hibernate Validator 的jar包,pom.xml中加入:
    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.9.Final</version>
    </dependency>
  • employee.java:在lastName上加上注解 @NotEmpty,在Email上加上注解: @Email,在birth上加上注解 @Past。
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Past;
import java.util.Date;

public class Employee {
    private Integer id ;

    @NotEmpty
    private String lastName ;

    @Email
    private String email ;

    private Integer gender ;
    private Department department ;


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

    @NumberFormat(pattern = "#,###,###.#")
    private Float salary ;


    public Float getSalary() {
        return salary;
    }

    public void setSalary(Float salary) {
        this.salary = salary;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }



    public Employee() {
    }

    public Employee(Integer id, String lastName, String email, Integer gender, Department department) {
        this.id = id;
        this.lastName = lastName;
        this.email = email;
        this.gender = gender;
        this.department = department;
    }

    public Employee(Integer id, String lastName, String email, Integer gender, Department department, Date birth) {
        this.id = id;
        this.lastName = lastName;
        this.email = email;
        this.gender = gender;
        this.department = department;
        this.birth = birth;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastName='" + lastName + '\'' +
                ", email='" + email + '\'' +
                ", gender=" + gender +
                ", department=" + department +
                ", birth=" + birth +
                ", salary=" + salary +
                '}';
    }
}
  • input.jsp页面:
<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
    <title>restful CRUD input</title>
</head>
<body>

    <form action="testConversionServiceConverer" method="post">
        <%[email protected]0-105--%>
        Employee: <input type="text" name="employee">
        <input type="submit" value="Submit">
    </form>

<%----%>
<form:form action="${pageContext.request.contextPath}/emp" method="post" modelAttribute="employee">
    <c:if test="${employee.id == null}">
    LastName: <form:input path="lastName"></form:input>
        <%--关注这里:错误消息会显示--%>
        <form:errors path="lastName" cssStyle="color: red"></form:errors>
    </c:if>
    <br>

    <c:if test="${employee.id != null}">
        <form:hidden path="id"></form:hidden>
        <input type="hidden" name="_method" value="PUT">
    </c:if>
    Email:<form:input path="email"></form:input>
     <%--关注这里:错误消息会显示--%>
    <form:errors path="email"></form:errors>
    <br>
    <%
        Map<String,String> genders = new HashMap<>() ;
        genders.put("0","Male") ;
        genders.put("1","Female") ;
        request.setAttribute("genders",genders);
    %>
    Gender:<form:radiobuttons path="gender" items="${genders}" delimiter="&nbsp;&nbsp;"></form:radiobuttons>
    <br>
    Department:<form:select path="department.id" items="${departments}" itemLabel="departmentName" itemValue="id" ></form:select>
    <br>
    Birth:<form:input path="birth"></form:input>
     <%--关注这里:错误消息会显示--%>
    <form:errors path="birth"></form:errors>
    <br>
    Salary:<form:input path="salary"></form:input>
    <input type="submit" value="Submit">
</form:form>

</body>
</html>
  • handlers: EmployeeHandler.java中:
@RequestMapping(value = "/emp",method = RequestMethod.POST)
    public String save(@Valid Employee employee , BindingResult result , Map<String,Object> map){

        if (result.getErrorCount() > 0 ){
            System.out.println("出错了:");
            for (FieldError error : result.getFieldErrors()){
                System.out.println(error.getField()+":"+error.getDefaultMessage());
            }
            //出错,转向指定页面
            map.put("departments",departmentDao.getDepartments()) ;
            return  "input" ;
        }
        System.out.println("save:"+employee);
        employeeDao.save(employee);
        return "redirect:/emps" ;
    }

提示消息的国际化

  • 每个属性在数据绑定和数据校验发生错误时,都会生成一个对应的 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:在数据绑定时,发生数据类型不匹配的问题
    methodInvocation:Spring MVC 在调用处理方法时发生了错误

  • 注册国际化资源文件

<!--配置国际化资源文件-->
    <bean  id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="i18n"></property>
    </bean>
  • i18n.properties
NotEmpty.employee.lastName=^^lastName不能为空
Email.employee.email=Email不合法
Past.employee.birth=Birth不能是一个将来的时间

猜你喜欢

转载自blog.csdn.net/hgx_suiyuesusu/article/details/80212162
今日推荐