1. 程式人生 > >SpringMVC 之validator驗證筆記

SpringMVC 之validator驗證筆記

SpringMVC支援的資料校驗是JSR303的標準,通過在bean的屬性上打上annotation @NotNull @Max等進行驗證。JSR303提供有很多annotation藉口,而SpringMVC對於這些驗證是使用hibernate的實現,所以我們需要新增hibernate的一個validator包:

<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.4.1.Final</version>
</dependency>

hibernate除了JSR303的標準之外還額外提供了其他的驗證annotation。

以下是JSR303和hibernate額外支援的驗證annotation,這個表是我在IBM官網找到的:

Bean Validation 中的 constraint

表 1. Bean Validation 中內建的 constraint

Constraint 詳細資訊
@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) 被註釋的元素必須符合指定的正則表示式

表 2. Hibernate Validator 附加的 constraint

Constraint 詳細資訊
@Email 被註釋的元素必須是電子郵箱地址
@Length 被註釋的字串的大小必須在指定的範圍內
@NotEmpty 被註釋的字串的必須非空
@Range 被註釋的元素必須在合適的範圍內

貼出一下IBM這篇文章的地址:https://www.ibm.com/developerworks/cn/java/j-lo-jsr303/

如果我們需要在SpringMVC中使用validator需要定義一個bean

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

然後 <mvc:annotation-driven />會自動掃描上下文去發現這個bean。

然後在相應的類屬性中繫結相關的驗證annotation,下面以User類為例:

@NumberFormat(pattern = "#,###.##")
@DecimalMax(value = "2000")
@DecimalMin(value = "1000")
@NotNull
private long balance;

使用Pattern註解可以直接繫結正則表示式:

@Pattern(regexp = "w{6,20}")
private String username;

以下是精簡過的User類:

public class User{

    private int userId;

    @Pattern(regexp = "w{6,20}")
    private String username;

    private String pwd;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date registerDate;

    @NumberFormat(pattern = "#,###.##")
    @DecimalMax(value = "2000")
    @DecimalMin(value = "1000")
    @NotNull
    private long balance;


}

然後我們可以在controller中進行驗證測試:

@RequestMapping("/validator.html")
public ModelAndView validatorTest(@Valid User user){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("user",user);
    modelAndView.setViewName("user/main");
    return modelAndView;
}

注意需要打上@Valid 註解標記需要進行屬性驗證。

我們嘗試去呼叫這個處理器會發現,只要驗證不成功就直接報錯:

但是我們並不希望這樣,希望如果在發現驗證錯誤的時候跳轉至合適的頁面,然後發現其錯誤並提示使用者,所以我們需要用到BindingResult類去獲得驗證的結果,BindingResult的常用方法有如下這些:

List<FieldError> getFieldErrors();  //獲得驗證失敗的欄位錯誤資訊

FieldError getFieldError(String var1);  //獲得相應屬性名的驗證錯誤資訊

Object getFieldValue(String var1);  //獲得屬性的值

int getErrorCount();   //錯誤的數量

boolean hasErrors();  //是否存在錯誤

所以我們可以改成這樣:

@RequestMapping("/validator.html")
public ModelAndView validatorTest(@Valid User user,BindingResult bindingResult){
    ModelAndView modelAndView = new ModelAndView();
    if (bindingResult.hasErrors()) {
        modelAndView.setViewName("user/login");
    } else {
        modelAndView.addObject("user", user);
        modelAndView.setViewName("user/main");
    }
    return modelAndView;
}

然後我們喜歡在頁面上顯示錯誤,可以使用SpringMVC提供的form標籤庫,我們就以登入頁面為例,雖然這個頁面的業務不太切合實際業務,但是為了方便演示就這樣寫:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html>
<head>
    <title>Register</title>
</head>
<body>
<form:form modelAttribute="user" method="post" action="formatter.html">
    <form:errors path="*"/>
    <p>userId:<input type="text" name="userId"/></p>
    <p>username:<input type="text" name="username"/></p>
    <p>password:<input type="password" name="password"/></p>
    <form:errors path="balance"/>
    <p>balance:<input type="text" name="balance"/></p>
    <form:errors path="registerDate"/>
    <p>registerDate:<input type="text" name="registerDate"/></p>
    <button type="submit">Login</button>

</form:form>
<img src="<c:url value="/test/test.jpeg" />"/>
</body>
</html>

注意幾個重點即可:

1、使用標籤庫

2、使用<form:from>的標籤然後定義這個提交的modelAttribute名稱。

3、將使用標籤<form:error path="相應的屬性">path屬性可以使用萬用字元*代表輸出全部錯誤。

4、在controller的處理方法中在user物件的入參中打上annotation @ModelAttribute,如下:

@RequestMapping("/formatter.html")
public ModelAndView userDetail(@ModelAttribute("user") @Valid User user, BindingResult bindingResult) {
    ModelAndView modelAndView = new ModelAndView();
    if (bindingResult.hasErrors()) {
        modelAndView.setViewName("user/login");
    } else {
        modelAndView.addObject("user", user);
        modelAndView.setViewName("user/main");
    }
    return modelAndView;
}

然後我們嘗試進行方法這個頁面,並輸入無法通過驗證的表單資料:

然後我們希望可以輸出的是中文,需要使用國際化,Spring也支援這一操作,只要新增一個bean:

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource" p:basename="conf/i18n/messages"></bean>

這裡bean有一個basename屬性,這個屬性就是相應的國際化配置檔案位置,以下是配置檔案的內容[檔案位置:conf/i18n/messages.properties]:

DecimalMin.user.balance=請輸入大於等於1000的數
DecimalMax.user.balance=請輸入小於等於2000的數

可以看到格式是<驗證的annotation名稱>.<modelAttribute名稱>.<驗證引數名稱>=相應的錯誤提示。

然後當再次測試時就會發現輸出的是中文的了。

最後說兩句:其實我們沒有必要在Spring中輸出這些錯誤提示,因為一般情況下這些驗證都會又前段JS完成,而且現在也有angularJS這種強大的JS框架足夠完成驗證工作,我們的驗證目的是避免有使用者沒有經過JS驗證直接提交引數,從業務安全性去考慮使用伺服器驗證,而不是為了在介面顯示這寫驗證提示資訊。而且現在也比較少直接提交form表單了,基本上大部分都是依賴於ajax。而且angularJS那種單頁面應用程式也受到很多開發的青睞,所以說我們最終伺服器驗證是作為一個最後防線,從而提供安全可靠的服務。