SpringMVC服務器驗證一種是有兩種方式,一種是基于Validator接口,一種是使用Annotaion JSR-303標準的驗證,下面主要是學習這兩種,工作中推薦后者,方便很多
一.基于Validator接口的驗證.
首先創建User實例,并加入幾個屬性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public class User { private String username; private String password; private String nickname; 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; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this .nickname = nickname; } @Override public String toString() { return "username--" +username+ "password--" +password+ "nickname--" +nickname; } } |
接著創建用于校檢的類UserValidator,讓其實現Validator,覆蓋其中的兩個方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import main.java.model.User; import org.springframework.validation.Errors; import org.springframework.validation.Validator; public class UserValidator implements Validator { @Override public boolean supports(Class<?> aClass) { //判斷是否是要校驗的類,這里是User return User. class .equals(aClass); } @Override public void validate(Object o, Errors errors) { User u = (User) o; if ( null == u.getPassword() || "" .equals(u.getPassword())){ //此方法可以加四個參數,第一個表單域field, //區分是哪個表單出錯,第二個errorCode錯誤碼, //第三個制定了資源文件中占位符,第四個具體錯誤返回信息 //簡寫版可以把2,3參數去掉 errors.rejectValue( "password" , null , null , "password is null" ); } } } |
上面的類只實現了對密碼判斷是否為空,為空則注冊這一錯誤信息,也就是”password is null”,接下來要實現控制器,控制器要做的事情,第一是注冊這個校驗器,第二是實現校驗.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
import main.java.model.User; ...... /** * 加上@Controller決定這個類是一個控制器 */ @Controller @RequestMapping ( "/user" ) public class HelloController { //我們知道在Controller類中通過@InitBinder標記的方法只有在請求當前Controller的時候才會被執行 //所以在這里注冊校驗器 @InitBinder public void initBainder(DataBinder binder){ binder.replaceValidators( new UserValidator()); } //這個方法主要是跳轉到登錄頁面 @RequestMapping (value = "/login" ,method = RequestMethod.GET) public String login(Model model){ model.addAttribute( new User()); return "user/login" ; } //處理登錄表單 @RequestMapping (value = "/login" ,method = RequestMethod.POST) public String login( @Validated User user, BindingResult br){ if (br.hasErrors()){ return "user/login" ; } return "--" ; } } |
上面代碼可以看到@Validated User user, BindingResult br這兩個參數,@Validated表明參數user是要校驗的類,BindingResult是存儲錯誤信息的類,兩者必須一一對應,并且位置挨著,不能中間有其他參數,
最后隨便寫一個jsp頁面實現校檢
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<%@ page contentType= "text/html;charset=UTF-8" language= "java" %> <%@taglib prefix= "sf" uri= "http://www.springframework.org/tags/form" %> <% request.setCharacterEncoding( "utf-8" ); %> <html> <head> <meta charset= "utf-8" > <title>用戶登錄</title> </head> <body> <sf:form modelAttribute= "user" method= "post" > 用戶名:<sf:input path= "username" /><sf:errors path= "username" /> <br> 密碼:<sf:input path= "password" /><sf:errors path= "password" /> <br> 昵稱:<sf:input path= "nickname" /><sf:errors path= "nickname" /> <br> <input type= "submit" value= "提交" > </sf:form> </body> </html> |
前面實現的是局部校驗,只對當前控制器有效,如果要實現全局校驗的話需要配置springMVC.xml文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<? xml version = "1.0" encoding = "UTF-8" ?> < beans xmlns = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:context = "http://www.springframework.org/schema/context" xmlns:mvc = "http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> < mvc:annotation-driven validator = "userValidator" /> < bean id = "userValidator" class = "com.xxx.xxx.UserValidator" /> ... </ beans > |
二.使用Annotaion JSR-303標準的驗證
使用這個需要導入支持JSR-303標準的包,建議使用hibernate Validator這個包,先看這個標準的原生標注
限制 | 說明 |
---|---|
@Null | 限制只能為null |
@NotNull | 限制必須不為null |
@AssertFalse | 限制必須為false |
@AssertTrue | 限制必須為true |
@DecimalMax(value) | 限制必須為一個不大于指定值的數字 |
@DecimalMin(value) | 限制必須為一個不小于指定值的數字 |
@Digits(integer,fraction) | 限制必須為一個小數,且整數部分的位數不能超過integer,小數部分的位數不能超過fraction |
@Future | 限制必須是一個將來的日期 |
@Max(value) | 限制必須為一個不大于指定值的數字 |
@Min(value) | 限制必須為一個不小于指定值的數字 |
@Past | 限制必須是一個過去的日期 |
@Pattern(value) | 限制必須符合指定的正則表達式 |
@Size(max,min) | 限制字符長度必須在min到max之間 |
@Past | 驗證注解的元素值(日期類型)比當前時間早 |
@NotEmpty | 驗證注解的元素值不為null且不為空(字符串長度不為0、集合大小不為0) |
@NotBlank | 驗證注解的元素值不為空(不為null、去除首位空格后長度為0),不同于@NotEmpty,@NotBlank只應用于字符串且在比較時會去除字符串的空格 |
驗證注解的元素值是Email,也可以通過正則表達式和flag指定自定義的email格式 |
要使用很簡單,在需要驗證的變量前面加上該Annotation即可,看下面使用后的User
1
2
3
4
5
6
7
8
9
|
public class User { @NotEmpty (message = "用戶名不能為空" ) private String username; @Size (min= 6 ,max= 20 ,message = "密碼長度不符合標準" ) private String password; private String nickname; ...... } |
然后再控制器里面加入驗證就可以了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Controller @RequestMapping ( "/user" ) public class HelloController { @RequestMapping (value = "/login" ,method = RequestMethod.GET) public String login(Model model){ model.addAttribute( new User()); return "user/login" ; } @RequestMapping (value = "/login" ,method = RequestMethod.POST) public String login( @Validated User user, BindingResult br){ if (br.hasErrors()){ return "user/login" ; } return "user/login" ; } } |
然后jsp頁面還是之前的頁面,驗證效果如下,這種方法明顯簡單多了
3.定義自己的Annotation Validator
這部分直接從[大牛][1]那拷貝過來的.
除了JSR-303原生支持的限制類型之外我們還可以定義自己的限制類型。定義自己的限制類型首先我們得定義一個該種限制類型的注解,而且該注解需要使用@Constraint標注。現在假設我們需要定義一個表示金額的限制類型,那么我們可以這樣定義:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; import com.xxx.xxx.constraint.impl.MoneyValidator; @Target ({ElementType.FIELD, ElementType.METHOD}) @Retention (RetentionPolicy.RUNTIME) @Constraint (validatedBy=MoneyValidator. class ) public @interface Money { String message() default "不是金額形式" ; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } |
我們可以看到在上面代碼中我們定義了一個Money注解,而且該注解上標注了@Constraint注解,使用@Constraint注解標注表明我們定義了一個用于限制的注解。@Constraint注解的validatedBy屬性用于指定我們定義的當前限制類型需要被哪個ConstraintValidator進行校驗。在上面代碼中我們指定了Money限制類型的校驗類是MoneyValidator。另外需要注意的是我們在定義自己的限制類型的注解時有三個屬性是必須定義的,如上面代碼所示的message、groups和payload屬性。
在定義了限制類型Money之后,接下來就是定義我們的限制類型校驗類MoneyValidator了。限制類型校驗類必須實現接口javax.validation.ConstraintValidator,并實現它的initialize和isValid方法。我們先來看一下MoneyValidator的代碼示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import java.util.regex.Pattern; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import com.xxx.xxx.constraint.Money; public class MoneyValidator implements ConstraintValidator<Money, Double> { private String moneyReg = "^\\d+(\\.\\d{1,2})?$" ; //表示金額的正則表達式 private Pattern moneyPattern = Pattern.compile(moneyReg); public void initialize(Money money) { // TODO Auto-generated method stub } public boolean isValid(Double value, ConstraintValidatorContext arg1) { // TODO Auto-generated method stub if (value == null ) return true ; return moneyPattern.matcher(value.toString()).matches(); } } |
從上面代碼中我們可以看到ConstraintValidator是使用了泛型的。它一共需要指定兩種類型,第一個類型是對應的initialize方法的參數類型,第二個類型是對應的isValid方法的第一個參數類型。從上面的兩個方法我們可以看出isValid方法是用于進行校驗的,有時候我們在校驗的過程中是需要取當前的限制類型的屬性來進行校驗的,比如我們在對@Min限制類型進行校驗的時候我們是需要通過其value屬性獲取到當前校驗類型定義的最小值的,我們可以看到isValid方法無法獲取到當前的限制類型Money。這個時候initialize方法的作用就出來了。
我們知道initialize方法是可以獲取到當前的限制類型的,所以當我們在校驗某種限制類型時需要獲取當前限制類型的某種屬性的時候,我們可以給當前的ConstraintValidator定義對應的屬性,然后在initialize方法中給該屬性賦值,接下來我們就可以在isValid方法中使用其對應的屬性了。針對于這種情況我們來看一個代碼示例,現在假設我要定義自己的@Min限制類型和對應的MinValidator校驗器,那么我可以如下定義:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
@Target ({ElementType.FIELD, ElementType.METHOD}) @Retention (RetentionPolicy.RUNTIME) @Constraint (validatedBy=MinValidator. class ) public @interface Min { int value() default 0 ; String message(); Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } MinValidator校驗器 public class MinValidator implements ConstraintValidator<Min, Integer> { private int minValue; public void initialize(Min min) { // TODO Auto-generated method stub //把Min限制類型的屬性value賦值給當前ConstraintValidator的成員變量minValue minValue = min.value(); } public boolean isValid(Integer value, ConstraintValidatorContext arg1) { // TODO Auto-generated method stub //在這里我們就可以通過當前ConstraintValidator的成員變量minValue訪問到當前限制類型Min的value屬性了 return value >= minValue; } } |
繼續來說一下ConstraintValidator泛型的第二個類型,我們已經知道它的第二個類型是對應的isValid的方法的第一個參數,從我給的參數名稱value來看也可以知道isValid方法的第一個參數正是對應的當前需要校驗的數據的值,而它的類型也正是對應的我們需要校驗的數據的數據類型。這兩者的數據類型必須保持一致,否則spring會提示找不到對應數據類型的ConstraintValidator。建立了自己的限制類型及其對應的ConstraintValidator后,其用法跟標準的JSR-303限制類型是一樣的。以下就是使用了上述自己定義的JSR-303限制類型——Money限制和Min限制的一個實體類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class User { private int age; private Double salary; @Min (value= 8 , message= "年齡不能小于8歲" ) public int getAge() { return age; } public void setAge( int age) { this .age = age; } @Money (message= "標準的金額形式為xxx.xx" ) public Double getSalary() { return salary; } public void setSalary(Double salary) { this .salary = salary; } } |
4.配合ajax驗證
最近寫的項目,感覺直接使用validator不太好用,主要是返回時會刷新整個頁面才會出來錯誤信息,體驗相當不好,驗證還是用ajax體驗比較好,所以配合ajax
思路:驗證還是使用springMVC來驗證,只是這次發現錯誤的話,把錯誤取出,存放到一個map中,然后ajax返回,頁面根據ajax返回值來判斷,從而顯示不同的信息
主要代碼:
1
2
3
4
5
6
7
|
if (br.hasErrors()){ //判斷是否有錯誤 //對錯誤集合進行遍歷,有的話,直接放入map集合中 br.getFieldErrors().forEach(p->{ maps.put(p.getField(),p.getDefaultMessage()); }); return maps; } |
這樣的話 maps里面存放的就是 錯誤變量名,錯誤信息,例如 username -‘用戶名不能為空'
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://blog.csdn.net/u012706811/article/details/51079740