day09-2-驗證以及國際化

2023-02-15 06:00:22

驗證以及國際化

1.概述

(1)概述

  1. 對於輸入的資料(比如表單資料),進行必要的驗證,並給出相應的提示資訊
  2. 對於驗證表單資料,SpringMVC 提供了很多使用的註解,這些註解由 JSR 303驗證框架提供。

(2)JSR 303 驗證框架

  1. JSR 303 是 Java 為 Bean 資料合法性校驗提供的標準框架,它已經包含在 JavaEE 中
  2. JSR 303 通過在 Bean 屬性上標註類似於 @NotNull、@Max 等標註的註解指定校驗規則,並通過標準的驗證介面對 Bean 進行驗證
  3. JSR 303 提供的基本驗證註解有:
註解 功能說明
@Null 被註釋的元素必須為null
@NotNull 被註釋的元素不能為null
@AssertTrue 被註釋的元素必須為true
@AssertFalse 被註釋的元素必須為false
@Min(value) 被註釋的元素必須是一個數位,其值必須大於等於指定的最小值
@Max(value) 被註釋的元素必須是一個數位,其值必須小於等於指定的最大值
@DecimalMin(value) 被註釋的元素必須是一個數位,其值必須大於等於指定的最小值
@DecimalMax(value) 被註釋的元素必須是一個數位,其值必須小於等於指定的最大值
@Size(max,min) 被註釋的元素的大小必須在指定的範圍內
@Digits(integer,fraction) 被註釋的元素必須是一個數位,其值必須在可接受的範圍內
@Past 被註釋的元素必須是一個過去的日期
@Future 被註釋的元素必須是一個將來的日期
@Pattern(value) 被註釋的元素必須符合指定的正規表示式

(3)Hibernate Validator 擴充套件註解

  1. Hibernate Validator 和 Hibernate 沒有關係,只是 JSR 303 實現的一個擴充套件

  2. Hibernate Validator 是 JSR 303的一個參考實現,除支援所有標準的校驗註解外,它還支援以下的擴充套件註解:

註解 功能說明
@Email 被註釋的元素必須是電子郵件地址
@Length 被註釋的字串的大小必須在指定的範圍內
@NotEmpty 被註釋的字串必須非空
@Range 被註釋的元素必須在合適的範圍內

2.應用範例

需求說明

指定表單的資料格式,後端在接收到資料後,能夠對資料進行校驗,並給不符合格式的資料返回提示資訊,顯示在前端頁面

image-20230214201222684

2.1程式碼實現

(1)引入驗證和國際化相關的jar包

image-20230214210353516

(2)Monster.java,屬性新增註解以驗證格式

package com.li.web.datavalid.entity;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;

import java.util.Date;

/**
 * @author 李
 * @version 1.0
 */
public class Monster {
    @NotEmpty
    private Integer id;

    @Email
    private String email;

    //表示接收到的age的值必須在1-100之間
    @Range(min = 1, max = 100)
    private Integer age;

    //Asserts that the annotated string, collection,
    // map or array is not {@code null} or empty.
    @NotEmpty
    private String name;

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

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

    public Monster() {
    }

    public Monster(Integer id, String email, Integer age, String name, Date birthday, Float salary) {
        this.id = id;
        this.email = email;
        this.age = age;
        this.name = name;
        this.birthday = birthday;
        this.salary = salary;
    }

    public Integer getId() {
        return id;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Float getSalary() {
        return salary;
    }

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

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

    public String getEmail() {
        return email;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Monster{" +
                "id=" + id +
                ", email='" + email + '\'' +
                ", age=" + age +
                ", name='" + name + '\'' +
                ", birthday=" + birthday +
                ", salary=" + salary +
                '}';
    }
}

(3)MonsterHandler.java

package com.li.web.datavalid;

import com.li.web.datavalid.entity.Monster;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.validation.Valid;
import java.util.Map;

/**
 * @author 李
 * @version 1.0
 */
@Controller
@Scope(value = "prototype")
public class MonsterHandler {
    /**
     * 1.SpringMVC可以將提交的資料,按照引數名和形參物件的屬性名匹配,
     * 然後直接封裝到物件中[模型資料]
     * 2.@Valid Monster monster 表示對monster接收的資料進行校驗
     * 3.校驗的發生的時機:在SpringMVC底層反射呼叫目標方法時,會接收到http請求接收到的資料,
     * 然後根據註解來進行驗證。在驗證過程中,如果出現了錯誤,就把錯誤資訊填充到errors和 map中
     * @param monster
     * @param errors  表示如果校驗出現了錯誤,會將校驗的錯誤資訊儲存到errors中
     * @param map     map不但會儲存monster物件,如果校驗出現錯誤,也會將校驗的錯誤資訊放到map中
     * @return
     */
    @RequestMapping(value = "/save", method = RequestMethod.POST)
    public String save(@Valid Monster monster, Errors errors, Map<String, Object> map) {
        System.out.println("----monster----" + monster);
        //為了檢視驗證的情況,輸出map和errors
        System.out.println("=======map=======");
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            System.out.println("key=" + entry.getKey() +
                    " value=" + entry.getValue());
            System.out.println("--------");
        }
        System.out.println("=======errors=======");
        for (ObjectError error : errors.getAllErrors()) {
            System.out.println("error="+error);
        }
   
        return "datavalid/success";
    }
}

(4)monster_addUI.jsp:

<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>新增妖怪</title>
</head>
<body>
<h3>新增妖怪</h3>
<form:form action="save" method="post" modelAttribute="monster">
    妖怪id:<form:input path="id"/><br/><br/>
    妖怪名字:<form:input path="name"/><br/><br/>
    妖怪年齡:<form:input path="age"/><br/><br/>
    妖怪生日:<form:input path="birthday"/> 要求以"yyyy-MM-dd"的格式<br/><br/>
    妖怪工資:<form:input path="salary"/> 要求以"###,###.##"的格式<br/><br/>
    電子郵件:<form:input path="email"/><br/><br/>
    <input type="submit" value="新增妖怪"/>
</form:form>
</body>
</html>

(5)測試

提交的資料:年齡這裡故意填寫不符合格式的資料(1-100)

image-20230214213251218

後臺輸出了預設的錯誤資訊:

----monster----Monster{id=1, email='[email protected]', age=999, name='king', birthday=Tue Jan 01 00:00:00 CST 1924, salary=1267.22}
=======map=======
key=monster value=Monster{id=1, email='[email protected]', age=999, name='king', birthday=Tue Jan 01 00:00:00 CST 1924, salary=1267.22}
--------
key=org.springframework.validation.BindingResult.monster value=org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'monster' on field 'age': rejected value [999]; codes [Range.monster.age,Range.age,Range.java.lang.Integer,Range]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [monster.age,age]; arguments []; default message [age],100,1]; default message [需要在1和100之間]
--------
=======errors=======
error=Field error in object 'monster' on field 'age': rejected value [999]; codes [Range.monster.age,Range.age,Range.java.lang.Integer,Range]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [monster.age,age]; arguments []; default message [age],100,1]; default message [需要在1和100之間]

(6)自定義錯誤資訊:設定 springDispatcherServlet-servlet.xml

<!--設定國際化錯誤資訊的資源處理 bean-->
<bean class="org.springframework.context.support.ResourceBundleMessageSource"
      id="messageSource">
    <!--
    設定國際化檔案名字
    如果下面這樣設定,表示 messageSource物件會到src/i18nXXX.properties 去讀取錯誤資訊
    -->
    <property name="basename" value="i18n"/>
</bean>

(7)在src 目錄下建立國際化檔案 i18n.properties

中文要使用 unicode 編碼處理

NotEmpty.monster.name=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a
typeMismatch.monster.age=\u5e74\u9f84\u8981\u6c42\u5728\u0031\u002d\u0031\u0035\u0030\u4e4b\u95f4
typeMismatch.monster.birthday=\u751f\u65e5\u683c\u5f0f\u4e0d\u6b63\u786e
typeMismatch.monster.salary=\u85aa\u6c34\u683c\u5f0f\u4e0d\u6b63\u786e

(8)修改 monster_addUI.jsp 的 form ,回顯錯誤資訊

<form:form action="save" method="post" modelAttribute="monster">
    妖怪id:<form:input path="id"/><form:errors path="id"/><br/><br/>
    妖怪名字:<form:input path="name"/><form:errors path="name"/><br/><br/>
    妖怪年齡:<form:input path="age"/><form:errors path="age"/><br/><br/>
    妖怪生日:<form:input path="birthday"/><form:errors path="birthday"/>
    要求以"yyyy-MM-dd"的格式<br/><br/>
    妖怪工資:<form:input path="salary"/><form:errors path="salary"/>
    要求以"###,###.##"的格式<br/><br/>
    電子郵件:<form:input path="email"/><form:errors path="email"/><br/><br/>
    <input type="submit" value="新增妖怪"/>
</form:form>

(9)再次進行測試

沒有在properties檔案中設定的提示,將會按照預設的錯誤資訊回顯

2.2細節說明和注意事項

  1. 在需要驗證的 Javabean/POJO 的欄位上新增相應的驗證註解

  2. 目標方法上,在 Javabean/POJO 型別的引數前,新增 @Valid 註解以告知 SpringMVC 該 Bean 是需要驗證的

  3. 在 @Valid 註解之後,新增一個 Errors 或 BindingResult 型別的引數,可以獲取到驗證的錯誤資訊

    校驗的發生的時機:SpringMVC 底層反射呼叫目標方法前,會接收到 http 請求接收到的資料,然後根據驗證註解來進行驗證。在驗證過程中,如果出現了錯誤,就把錯誤資訊填充到 errors,map 等引數中

  4. 需要使用 <form:errors path="xxx"></form:errors> 標籤來顯示錯誤資訊,該標籤需要寫在 <form:form> 標籤內生效

  5. 自定義錯誤訊息的國際化檔案 i18n.properties,如果是中文需要使用 unicode 編碼處理。

    格式為:驗證規則.表單modelAttribute值.屬性名=錯誤提示資訊

    image-20230214222905106
  6. 注意@NotNull 和 @NotEmpty 的區別

    @NotEmpty:

    Asserts that the annotated string, collection, map or array is not {@code null} or empty.
    

    @NotNull:

    The annotated element must not be {@code null}. Accepts any type.
    

    如果是字串驗證空,建議使用 @NotEmpty

  7. SpringMVC 驗證時,同一個屬性,會根據不同的驗證錯誤,返回不同的錯誤資訊

3.註解的結合使用