1. 程式人生 > >SpringMVC學習記錄(六)--Validator驗證

SpringMVC學習記錄(六)--Validator驗證

SpringMVC伺服器驗證一種是有兩種方式,一種是基於Validator介面,一種是使用Annotaion JSR-303標準的驗證,下面主要是學習這兩種,工作中推薦後者,方便很多

一.基於Validator介面的驗證.

首先建立User例項,並加入幾個屬性

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,覆蓋其中的兩個方法

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”,接下來要實現控制器,控制器要做的事情,第一是註冊這個校驗器,第二是實現校驗.

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頁面實現校檢

<%@ 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檔案

<?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 驗證註解的元素值是Email,也可以通過正則表示式和flag指定自定義的email格式

要使用很簡單,在需要驗證的變數前面加上該Annotation即可,看下面使用後的User

public class User {
    @NotEmpty(message = "使用者名稱不能為空")
    private String username;
    @Size(min=6 ,max= 20 ,message = "密碼長度不符合標準")
    private String password;
    private String nickname;

   ......
}

然後再控制器裡面加入驗證就可以了

@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標註。現在假設我們需要定義一個表示金額的限制類型,那麼我們可以這樣定義:


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的程式碼示例:


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校驗器,那麼我可以如下定義:

@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限制的一個實體類:

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返回值來判斷,從而顯示不同的資訊

主要程式碼:

 if (br.hasErrors()){//判斷是否有錯誤
             //對錯誤集合進行遍歷,有的話,直接放入map集合中
            br.getFieldErrors().forEach(p->{
                maps.put(p.getField(),p.getDefaultMessage());
            });
            return maps;
        }

這樣的話 maps裡面存放的就是 錯誤變數名,錯誤資訊,例如 username -‘使用者名稱不能為空’