驗證以及國際化
1.概述
(1)概述
- 對於輸入的資料(比如表單資料),進行必要的驗證,並給出相應的提示資訊
- 對於驗證表單資料,SpringMVC 提供了很多使用的註解,這些註解由 JSR 303驗證框架提供。
(2)JSR 303 驗證框架
- JSR 303 是 Java 為 Bean 資料合法性校驗提供的標準框架,它已經包含在 JavaEE 中
- JSR 303 透過在 Bean 屬性上標註類似於 @NotNull、@Max 等標註的註解指定校驗規則,並透過標準的驗證介面對 Bean 進行驗證
- 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 擴充套件註解
-
Hibernate Validator 和 Hibernate 沒有關係,只是 JSR 303 實現的一個擴充套件
-
Hibernate Validator 是 JSR 303的一個參考實現,除支援所有標準的校驗註解外,它還支援以下的擴充套件註解:
註解 | 功能說明 |
---|---|
被註釋的元素必須是電子郵件地址 | |
@Length | 被註釋的字串的大小必須在指定的範圍內 |
@NotEmpty | 被註釋的字串必須非空 |
@Range | 被註釋的元素必須在合適的範圍內 |
2.應用例項
需求說明
指定表單的資料格式,後端在接收到資料後,能夠對資料進行校驗,並給不符合格式的資料返回提示資訊,顯示在前端頁面
2.1程式碼實現
(1)引入驗證和國際化相關的jar包
(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)
後臺輸出了預設的錯誤資訊:
----monster----Monster{id=1, email='king@sohu.com', age=999, name='king', birthday=Tue Jan 01 00:00:00 CST 1924, salary=1267.22}
=======map=======
key=monster value=Monster{id=1, email='king@sohu.com', 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細節說明和注意事項
-
在需要驗證的 Javabean/POJO 的欄位上新增相應的驗證註解
-
目標方法上,在 Javabean/POJO 型別的引數前,新增 @Valid 註解以告知 SpringMVC 該 Bean 是需要驗證的
-
在 @Valid 註解之後,新增一個 Errors 或 BindingResult 型別的引數,可以獲取到驗證的錯誤資訊
校驗的發生的時機:SpringMVC 底層反射呼叫目標方法前,會接收到 http 請求接收到的資料,然後根據驗證註解來進行驗證。在驗證過程中,如果出現了錯誤,就把錯誤資訊填充到 errors,map 等引數中
-
需要使用
<form:errors path="xxx"></form:errors>
標籤來顯示錯誤資訊,該標籤需要寫在<form:form>
標籤內生效 -
自定義錯誤訊息的國際化檔案 i18n.properties,如果是中文需要使用 unicode 編碼處理。
格式為:
驗證規則.表單modelAttribute值.屬性名=錯誤提示資訊
-
注意@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
-
SpringMVC 驗證時,同一個屬性,會根據不同的驗證錯誤,返回不同的錯誤資訊