1、 JSR-303
JSR-303 是JAVA EE 6 中的一項子規範,叫做Bean Validation,官方參考實現是Hibernate Validator。
此實現與Hibernate ORM 沒有任何關係。JSR 303 用於對Java Bean 中的字段的值進行驗證。
Spring MVC 3.x之中也大力支持 JSR-303,可以在控制器中對錶單提交的數據方便地驗證。
JSR 303內置的約束規則:
- @AssertTrue / @AssertFalse
驗證適用字段:boolean
註解說明:驗證值是否爲true / false
屬性說明:-
- @DecimalMax / @DecimalMin
驗證適用字段:BigDecimal,BigInteger,String,byte,short,int,long
註解說明:驗證值是否小於或者等於指定的小數值,要注意小數存在精度問題
屬性說明:公共
- @Digits
驗證適用字段:BigDecimal,BigInteger,String,byte,short,int,long
註解說明:驗證值的數字構成是否合法
屬性說明:integer:指定整數部分的數字的位數。fraction: 指定小數部分的數字的位數。
- @Future / @Past
驗證適用字段:Date,Calendar
註解說明:驗證值是否在當前時間之後 / 之前
屬性說明:公共
- @Max / @Min
驗證適用字段:BigDecimal,BigInteger,String,byte,short,int,long
註解說明:驗證值是否小於或者等於指定的整數值
屬性說明:公共
- @NotNull / @Null
驗證適用字段:引用數據類型
註解說明:驗證值是否爲非空 / 空
屬性說明:公共
- @Pattern
驗證適用字段:String
註解說明:驗證值是否配備正則表達式
屬性說明:regexp:正則表達式flags: 指定Pattern.Flag 的數組,表示正則表達式的相關選項。
- @Size
驗證適用字段:String,Collection,Map,數組
註解說明:驗證值是否滿足長度要求
屬性說明:max:指定最大長度,min:指定最小長度。
- @Valid
驗證適用字段:引用類型
註解說明:驗證值是否需要遞歸驗證
屬性說明:無
使用Spring MVC 和 JSR-303的標註做表單提交的服務器端驗證時,@Valid 標註的Command對象和BindingResult參數一定要緊挨着。要不然數據綁定錯誤直接拋異常,不會封裝成一個BindingResult對象。
2、Spring MVC數據校驗
<mvc:annotation-driven/>會默認裝配好一個LocalValidatorFactoryBean,通過在處理方法的入參上標註@Valid註解就可讓Spring MVC在完成數據綁定後執行數據校驗操作。
A、需要的額外的JAR包
除了Spring MVC本身的JAR包外,我們還需要一些額外的JAR包。
B、添加JSR 303註解
package cn.framelife.mvc.entity;
import java.util.Date;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;
public class User implements java.io.Serializable {
private Integer id;
/*
* 測試的時候,發現在頁面獲取到的數據傳到User模式後,都是非空的。
*/
@NotNull(message = "用戶名不能爲空")
private String username;
@Size(min = 2, max = 6, message = "長度是2-6之間")
private String password;
@Past(message = "必須是一個過去的時間")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@DecimalMin(value = "1000.00", message = "工資必須大於1000.00")
@DecimalMax(value = "10000.00", message = "工資必須小於10000.00")
@NumberFormat(pattern = "#,###.##")
private long salary;
@Pattern(regexp = "1[3|4|5|8][0-9]\\d{4,8}", message = "手機號碼不匹配")
private String phone;
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public long getSalary() {
return salary;
}
public void setSalary(long salary) {
this.salary = salary;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
C、Controller中的處理
@Controller
@RequestMapping("/user")
public class UserControl {
/**
* 此方法是用於第一次給頁面添加一個user模型數據的
*/
@RequestMapping(value="/add",method = RequestMethod.GET)
public ModelAndView initForm(){
User user = new User();
return new ModelAndView("/add").addObject(user);
}
/**
* 給入參的模型對象User加入@Valid註解,說明這個對象裏面的屬性需要校驗
* BindingResult入參是用以判斷上面的校驗是否出錯
*/
@RequestMapping("create")
public ModelAndView createUser(@Valid User user,BindingResult bindingResult){
ModelAndView view = new ModelAndView();
System.out.println(user.getUsername()+"----");
//如果校驗有問題,跑回原來的add.jsp頁面,如果沒問題跑到success.jsp頁面
if(bindingResult.hasErrors()){
view.setViewName("/add");
}else{
view.setViewName("/success");
}
return view;
}
}
D、頁面輸入及輸出
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!-- 導入spring的表單標籤庫 -->
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>增加</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<!-- 在使用表單標籤的時候需要獲取到一個user模型數據,那我們不能直接訪問頁面(通過/user/add.abc訪問),必須通過訪問一次服務器,獲取到一個user數據模型後才行,否則會出錯 -->
<form:form modelAttribute="user" action="user/create.abc">
<!-- 輸出所有的錯誤信息 -->
<form:errors path="*" /><br/>
<hr/>
<!-- 輸出單個錯誤信息 -->
<form:errors path="username" ></form:errors><br/>
用戶名:<form:input path="username"/><br/>
<form:errors path="password" ></form:errors><br/>
密 碼:<form:password path="password"/><br/>
<form:errors path="birthday" ></form:errors><br/>
生日:<form:input path="birthday"/><br/>
<form:errors path="salary" ></form:errors><br/>
工資:<form:input path="salary"/><br/>
<form:errors path="phone" ></form:errors><br/>
電話:<form:input path="phone"/><br/>
<input type="submit">
</form:form>
</body>
</html>
3、通過國際化資源顯示錯誤信息
A、校驗屬性
/*
* 使用資源文件,這裏不再需要message參數
*/
@Pattern(regexp = "1[3|4|5|8][0-9]\\d{4,8}")
private String phone;
B、資源文件(src下的messages.properties)
Pattern.user.phone=\u624B\u673A\u53F7\u7801\u4E0D\u5339\u914D
上面鍵值對中,
鍵是校驗類javax.validation.constraints.Pattern的類名+被校驗的模型對象名+屬性名
值是”手機號碼不匹配”
C、配置本地化資源文件(mvc-servlet.xml)
<!-- 配置國際化資源文件 -->
<!--
ReloadableResourceBundleMessageSource加載時,默認使用DefaultResourceLoader,他會先判斷資源path是否帶有classpath:前綴,如果有,用ClassPathResource去加載資源文件,如果沒有試着用文件協議的url去訪問,再沒有就在contextPath即WEB-INF下查找
-->
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
p:basename="classpath:messages">
</bean>
D、顯示
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!-- 導入spring的表單標籤庫 -->
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>增加</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<!-- 在使用表單標籤的時候需要獲取到一個user模型數據,那我們不能直接訪問頁面(通過/user/add.abc訪問),必須通過訪問一次服務器,獲取到一個user數據模型後才行,否則會出錯 -->
<form:form modelAttribute="user" action="user/create.abc">
<!-- 輸出所有的錯誤信息 -->
<form:errors path="*" /><br/>
<hr/>
<!-- 輸出單個錯誤信息 -->
<form:errors path="username" ></form:errors><br/>
用戶名:<form:input path="username"/><br/>
<form:errors path="password" ></form:errors><br/>
密 碼:<form:password path="password"/><br/>
<form:errors path="birthday" ></form:errors><br/>
生日:<form:input path="birthday"/><br/>
<form:errors path="salary" ></form:errors><br/>
工資:<form:input path="salary"/><br/>
<form:errors path="phone" ></form:errors><br/>
電話:<form:input path="phone"/><br/>
<input type="submit">
</form:form>
</body>
</html>