SpringMVC服務器端校驗-無配置文件
來源:程序員人生 發布時間:2016-07-26 13:48:56 閱讀次數:3503次
使用Validator接口進行驗證
在SpringMVC中提供了1個Validator接口,我們可以通過該接口來定義我們自己對實體對象的驗證。接下來看1個示例。
假定我們現在有1個需要進行驗證的實體類User,其代碼以下所示:
Java代碼 
- public class User {
-
- private String username;
-
- private String password;
-
- 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 toString() {
- return username + ", " + password;
- }
-
- }
那末當我們需要使用SpringMVC提供的Validator接口來對該實體類進行校驗的時候該如何做呢?這個時候我們應當提供1個Validator的實現類,并實現Validator接口的supports方法和validate方法。Supports方法用于判斷當前的Validator實現類是不是支持校驗當前需要校驗的實體類,只有當supports方法的返回結果為true的時候,該Validator接口實現類的validate方法才會被調用來對當前需要校驗的實體類進行校驗。這里假定我們需要驗證User類的username和password都不能為空,先給出其代碼,稍后再進行解釋。這里我們定義1個UserValidator,其代碼以下:
Java代碼 
- import org.springframework.validation.Errors;
- import org.springframework.validation.ValidationUtils;
- import org.springframework.validation.Validator;
-
- public class UserValidator implements Validator {
-
- public boolean supports(Class<?> clazz) {
- // TODO Auto-generated method stub
- return User.class.equals(clazz);
- }
-
- public void validate(Object obj, Errors errors) {
- // TODO Auto-generated method stub
- ValidationUtils.rejectIfEmpty(errors, "username", null, "Username is empty.");
- User user = (User) obj;
- if (null == user.getPassword() || "".equals(user.getPassword()))
- errors.rejectValue("password", null, "Password is empty.");
- }
-
- }
在上述代碼中我們在supports方法中定義了該UserValidator只支持對User對象進行校驗。在validate方法中我們校驗了User對象的username和password不為empty的情況,這里的empty包括null和空字符串兩種情況。ValidationUtils類是Spring中提供的1個工具類。Errors就是Spring用來寄存毛病信息的對象。
我們已定義了1個對User類進行校驗的UserValidator了,但是這個時候UserValidator還不能對User對象進行校驗,由于我們還沒有告知Spring應當使用UserValidator來校驗User對象。在SpringMVC中我們可使用DataBinder來設定當前Controller需要使用的Validator。先來看下面1段代碼:
Java代碼 
- import javax.validation.Valid;
- import org.springframework.stereotype.Controller;
- import org.springframework.validation.BindingResult;
- import org.springframework.validation.DataBinder;
- import org.springframework.web.bind.annotation.InitBinder;
- import org.springframework.web.bind.annotation.RequestMapping;
-
- @Controller
- public class UserController {
-
- @InitBinder
- public void initBinder(DataBinder binder) {
- binder.setValidator(new UserValidator());
- }
-
- @RequestMapping("login")
- public String login(@Valid User user, BindingResult result) {
- if (result.hasErrors())
- return "redirect:user/login";
- return "redirect:/";
- }
-
- }
在上面這段代碼中我們可以看到我們定義了1個UserController,該Controller有1個處理login操作的處理器方法login,它需要接收客戶端發送的1個User對象,我們就是要利用前面的UserValidator對該User對象進行校驗。首先我們可以看到我們login方法接收的參數user是用@Valid進行標注的,這里的@Valid是定義在JSR⑶03標準中的,我這里使用的是Hibernate Validation對它的實現。這里我們必須使用@Valid標注我們需要校驗的參數user,否則Spring不會對它進行校驗。另外我們的處理器方法必須給定包括Errors的參數,這可以是Errors本身,也能夠是它的子類BindingResult,使用了Errors參數就是告知Spring關于表單對象數據校驗的毛病將由我們自己來處理,否則Spring會直接拋出異常,而且這個參數是必須緊挨著@Valid參數的,即必須緊挨著需要校驗的參數,這就意味著我們有多少個@Valid參數就需要有多少個對應的Errors參數,它們是逐一對應的。前面有提到我們可以通過DataBinder來指定需要使用的Validator,我們可以看到在上面代碼中我們通過@InitBinder標記的方法initBinder設置了當前Controller需要使用的Validator是UserValidator。這樣當我們要求處理器方法login時就會使用DataBinder設定的UserValidator來校驗當前的表單對象User,首先會通過UserValidator的supports方法判斷其是不是支持User對象的校驗,若支持則調用UserValidator的validate方法,并把相干的校驗信息寄存到當前的Errors對象中。接著我們就能夠在我們的處理器方法中根據是不是有校驗異常信息來做不同的操作。在上面代碼中我們定義了在有異常信息的時候就跳轉到登陸頁面。這樣我們就能夠在登陸頁面上通過errors標簽來展現這些毛病信息了。
我們知道在Controller類中通過@InitBinder標記的方法只有在要求當前Controller的時候才會被履行,所以其中定義的Validator也只能在當前Controller中使用,如果我們希望1個Validator對所有的Controller都起作用的話,我們可以通過WebBindingInitializer的initBinder方法來設定了。另外,在SpringMVC的配置文件中通過mvc:annotation-driven的validator屬性也能夠指定全局的Validator。代碼以下所示:
Xml代碼 
- <?xml version="1.0" encoding="UTF⑻"?>
- <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⑶.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context⑶.0.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc⑶.0.xsd">
-
- <mvc:annotation-driven validator="userValidator"/>
-
- <bean id="userValidator" class="com.xxx.xxx.UserValidator"/>
-
- ...
- </beans>
使用JSR⑶03 Validation進行驗證
JSR⑶03是1個數據驗證的規范,這里我不會講這個規范是怎樣回事,只會講1下JSR⑶03在SpringMVC中的利用。JSR⑶03只是1個規范,而Spring也沒有對這1規范進行實現,那末當我們在SpringMVC中需要使用到JSR⑶03的時候就需要我們提供1個對JSR⑶03規范的實現,Hibernate Validator是實現了這1規范的,這里我將以它作為JSR⑶03的實現來說解SpringMVC對JSR⑶03的支持。
JSR⑶03的校驗是基于注解的,它內部已定義好了1系列的限制注解,我們只需要把這些注解標記在需要驗證的實體類的屬性上或是其對應的get方法上。來看以下1個需要驗證的實體類User的代碼:
Java代碼 
-
- import javax.validation.constraints.Min;
- import javax.validation.constraints.NotNull;
- import org.hibernate.validator.constraints.NotBlank;
-
- public class User {
-
- private String username;
-
- private String password;
-
- private int age;
-
- @NotBlank(message="用戶名不能為空")
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- @NotNull(message="密碼不能為null")
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- @Min(value=10, message="年齡的最小值為10")
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- }
我們可以看到我們在username、password和age對應的get方法上都加上了1個注解,這些注解就是JSR⑶03里面定義的限制,其中@NotBlank是Hibernate Validator的擴大。不難發現,使用JSR⑶03來進行校驗比使用Spring提供的Validator接口要簡單的多。我們知道注解只是起到1個標記性的作用,它是不會直接影響到代碼的運行的,它需要被某些類辨認到才能起到限制作用。使用SpringMVC的時候我們只需要把JSR⑶03的實現者對應的jar包放到classpath中,然后在SpringMVC的配置文件中引入MVC
Namespace,并加上<mvn:annotation-driven/>就能夠非常方便的使用JSR⑶03來進行實體對象的驗證。加上了<mvn:annotation-driven/>以后Spring會自動檢測classpath下的JSR⑶03提供者并自動啟用對JSR⑶03的支持,把對應的校驗毛病信息放到Spring的Errors對象中。這時候候SpringMVC的配置文件以下所示:
Xml代碼 
- <?xml version="1.0" encoding="UTF⑻"?>
- <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⑶.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context⑶.0.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc⑶.0.xsd">
-
- <mvc:annotation-driven/>
- </beans>
接著我們來定義1個使用User對象作為參數接收者的Controller,其代碼以下所示:
Java代碼 
- import javax.validation.Valid;
- import org.springframework.stereotype.Controller;
- import org.springframework.validation.BindingResult;
- import org.springframework.web.bind.annotation.RequestMapping;
-
- @Controller
- public class UserController {
-
- @RequestMapping("login")
- public String login(@Valid User user, BindingResult result) {
- if (result.hasErrors())
- return "user/login";
- return "redirect:/";
- }
-
- }
這樣當我們不帶任何參數要求login.do的時候就不能通過實體對象User的屬性數據有效性限制,然后會把對應的毛病信息放置在當前的Errors對象中。
JSR⑶03原生支持的限制有以下幾種:
限制
|
說明
|
@Null
|
限制只能為null
|
@NotNull
|
限制必須不為null
|
@AssertFalse
|
限制必須為false
|
@AssertTrue
|
限制必須為true
|
@DecimalMax(value)
|
限制必須為1個不大于指定值的數字
|
@DecimalMin(value)
|
限制必須為1個不小于指定值的數字
|
@Digits(integer,fraction)
|
限制必須為1個小數,且整數部份的位數不能超過integer,小數部份的位數不能超過fraction
|
@Future
|
限制必須是1個將來的日期
|
@Max(value)
|
限制必須為1個不大于指定值的數字
|
@Min(value)
|
限制必須為1個不小于指定值的數字
|
@Past
|
限制必須是1個過去的日期
|
@Pattern(value)
|
限制必須符合指定的正則表達式
|
@Size(max,min)
|
限制字符長度必須在min到max之間
|
除JSR⑶03原生支持的限制類型以外我們還可以定義自己的限制類型。定義自己的限制類型首先我們得定義1個該種限制類型的注解,而且該注解需要使用@Constraint標注。現在假定我們需要定義1個表示金額的限制類型,那末我們可以這樣定義:
Java代碼 
-
- 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 {};
-
- }
我們可以看到在上面代碼中我們定義了1個Money注解,而且該注解上標注了@Constraint注解,使用@Constraint注解標注表明我們定義了1個用于限制的注解。@Constraint注解的validatedBy屬性用于指定我們定義確當前限制類型需要被哪一個ConstraintValidator進行校驗。在上面代碼中我們指定了Money限制類型的校驗類是MoneyValidator。另外需要注意的是我們在定義自己的限制類型的注解時有3個屬性是必須定義的,如上面代碼所示的message、groups和payload屬性。
在定義了限制類型Money以后,接下來就是定義我們的限制類型校驗類MoneyValidator了。限制類型校驗類必須實現接口javax.validation.ConstraintValidator,并實現它的initialize和isValid方法。我們先來看1下MoneyValidator的代碼示例:
Java代碼 
-
- 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是使用了泛型的。它1共需要指定兩種類型,第1個類型是對應的initialize方法的參數類型,第2個類型是對應的isValid方法的第1個參數類型。從上面的兩個方法我們可以看出isValid方法是用于進行校驗的,有時候我們在校驗的進程中是需要取當前的限制類型的屬性來進行校驗的,比如我們在對@Min限制類型進行校驗的時候我們是需要通過其value屬性獲得到當前校驗類型定義的最小值的,我們可以看到isValid方法沒法獲得到當前的限制類型Money。這個時候initialize方法的作用就出來了。我們知道initialize方法是可以獲得到當前的限制類型的,所以當我們在校驗某種限制類型時需要獲得當前限制類型的某種屬性的時候,我們可以給當前的ConstraintValidator定義對應的屬性,然后在initialize方法中給該屬性賦值,接下來我們就能夠在isValid方法中使用其對應的屬性了。針對這類情況我們來看1個代碼示例,現在假定我要定義自己的@Min限制類型和對應的MinValidator校驗器,那末我可以以下定義:
Min限制類型
Java代碼 
- @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校驗器
Java代碼 
- 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;
- }
-
- }
繼續來講1下ConstraintValidator泛型的第2個類型,我們已知道它的第2個類型是對應的isValid的方法的第1個參數,從我給的參數名稱value來看也能夠知道isValid方法的第1個參數正是對應確當前需要校驗的數據的值,而它的類型也正是對應的我們需要校驗的數據的數據類型。這二者的數據類型必須保持1致,否則Spring會提示找不到對應數據類型的ConstraintValidator。建立了自己的限制類型及其對應的ConstraintValidator后,其用法跟標準的JSR⑶03限制類型是1樣的。以下就是使用了上述自己定義的JSR⑶03限制類型——Money限制和Min限制的1個實體類:
Java代碼 
- 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")
-
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈