SpringMVC4+thymeleaf3的一個簡單實例(篇四:form表單數據驗證)


關於表單數據驗證有很多中方法,這里我僅介紹JSR303注解驗證。
JSR303僅僅是一個規范,這里我們要用到它的一個實現:hibernate-validator。


注意在spring的配置文件spring-mvc.xml中要有這句代碼:<mvc:annotation-driven/>,有了它,spring框架會自動加載classpath的jsr303的實現。

開工之前,我們需要引入以下lib文件到WEB-INF/lib,並添加到classpath:
validation-api-1.1.0.Final.jar
classmate-1.3.1.jar
jboss-logging-3.3.0.Final.jar
hibernate-validator-5.3.2.Final.jar
以上jar文件都在hibernate-validator-5.3.2.Final-dist.zip這里,官網下載http://hibernate.org/validator/

延續前面篇節的內容。

一、修改AnimalForm.Java類,在oname,ocount,memo字段上分別加上驗證注解,代碼如下:

 1     package com.zoo.web.form;  
 2       
 3     import javax.validation.constraints.NotNull;  
 4     import javax.validation.constraints.Size;  
 5       
 6     import org.hibernate.validator.constraints.NotEmpty;  
 7     import org.hibernate.validator.constraints.Range;  
 8       
 9     public class AnimalForm {  
10       
11         private long id;  
12           
13         @NotEmpty(message="動物名: 不能為空")   
14         private String oname;  
15           
16         @Range(min = 1, message="數量: 必須大於0")  
17         @NotNull(message="數量: 不能為空")  
18         private int ocount;  
19           
20         @Size(max = 10, message="備注: 長度不能超過10個字符")  
21         private String memo;  
22       
23         /** 省略getter和setter **/  
24       
25           
26     }  

解釋
@NotEmpty:這個注解表示檢查oname字段是不是為空字符串""或者是不是為null,如果是則給出提示信息:"動物名:不能為空"。
它支持的類型包括:字符序列CharSequence(CharBuffer, Segment, String, StringBuffer, StringBuilder);集合Collection(ArrayList, HashSet, Stack, Vector等,很多);Map以及數組arrays。它將檢查所給對象的是不是為empty或者null,empty也就是長度為0,對於字符串來說就是""。

@NotNull:檢查所標注元素ocount不能為null,如果是則給出提示信息:“數量:不能為空”。
它支持任意類型,檢查標注對象是否為null。注意和@NotEmpty的區別,她不檢查對象是不是為empty。empty對於字符串來說是空字符串,對於集合以及map或數組來說就是所含元素數量為0。

@Range(min=, max=):表示ocount元素的最小值是1,如果小於1,則給出信息:“數量:必須大於0”。
支持類型:BigDecimal, BigInteger, CharSequence, byte, short, int, long 以及這些原始類型對應的wrapper(包裝類)。它將檢查所給對象的值是不是大於等於min且小於等於max。

@Size(min=, max=):檢查memo對象的長度不能超過10, 否則提示:“備注:長度不能超過10個字符”。
適用於CharSequence, Collection, Map 以及數組,檢查標注對象的size是大於等於min並且小於等於max。

注意這么做驗證是有問題的,比如oname輸入幾個空格它會驗證通過,而對於ocount,在輸入整數的情況下這完全沒有問題,但是如果我們輸入帶小數點的數字或者輸入非數字,或者空字符串的時候程序就會出現exception,這不是我們所希望的,具體的改進代碼我們在篇末說明。

關於hibernate validator詳細介紹請參閱 reference http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/

二、修改ZooController里的doAdd方法

 1     @RequestMapping(path = "/list", params = {"save"}, method = RequestMethod.POST)  
 2         public String doAdd(Model model, @Valid AnimalForm form, BindingResult result){  
 3             System.out.println("動物名:" + form.getOname());  
 4             System.out.println("數量:" + form.getOcount());  
 5             System.out.println("備注:" + form.getMemo());  
 6             if(result.hasErrors()){  
 7                 model.addAttribute("MSG", "出錯啦!");  
 8             }else{  
 9                 model.addAttribute("MSG", "提交成功!");  
10             }  
11             return "zoolist";  
12         }  

解釋:
方法中Model參數,用於存放任意數據以便傳遞到頁面,注意Model僅僅是一個接口,spring框架會幫我們實例化具體的類並設置到該方法當中;上例我們在該model里放了一個key為“MSG”的attribute,頁面上通過表達式就可以取得其值。

@Valid AnimalForm form,@Valid表示要對該form進行驗證,具體驗證規則就是根據上面【一】里提到;spring框架會根據字段名稱將頁面傳遞過來的值綁定到animalForm中。

BindingResult result,spring框架會將驗證結果設置到該參數,並將該參數放到model傳遞給頁面。

springMVC是非常靈活的,以下幾種寫法可以達到同樣的效果:
(1)

 1     @RequestMapping(path = "/list", params = {"save"}, method = RequestMethod.POST)  
 2         public ModelAndView doAdd(@Valid AnimalForm form, BindingResult result){  
 3             ModelAndView model = new ModelAndView();  
 4             System.out.println("動物名:" + form.getOname());  
 5             System.out.println("數量:" + form.getOcount());  
 6             System.out.println("備注:" + form.getMemo());  
 7             if(result.hasErrors()){  
 8                 model.addObject("MSG", "出錯啦!");  
 9             }else{  
10                 model.addObject("MSG", "提交成功!");  
11             }  
12             model.setViewName("zoolist");  
13             return model;  
14         }  

(2)

 1     @RequestMapping(path = "/list", params = {"save"}, method = RequestMethod.POST)  
 2         public ModelAndView doAdd(ModelAndView model, @Valid AnimalForm form, BindingResult result){  
 3             System.out.println("動物名:" + form.getOname());  
 4             System.out.println("數量:" + form.getOcount());  
 5             System.out.println("備注:" + form.getMemo());  
 6             if(result.hasErrors()){  
 7                 model.addObject("MSG", "出錯啦!");  
 8             }else{  
 9                 model.addObject("MSG", "提交成功!");  
10             }  
11             model.setViewName("zoolist");  
12             return model;  
13         }  

(3)

1     @RequestMapping(path = "/list", params = {"save"}, method = RequestMethod.POST)  
2         public String doAdd(@Valid AnimalForm form, BindingResult result){  
3             System.out.println("動物名:" + form.getOname());  
4             System.out.println("數量:" + form.getOcount());  
5             System.out.println("備注:" + form.getMemo());  
6       
7             return "zoolist";  
8         }  

注意(1)(2)僅僅是ModelAndView實例化的方式不同而已,一個是自己手動實例化,一個是框架實例化;


(3)中我們去掉了model參數,但這並不影響我們的驗證以及將驗證結果傳遞到頁面,只不過是你不能通過model設置一些attribute到頁面了。


這里說一下Model和ModelAndView的區別:
Model主要用於將數據傳遞到頁面,一般采用model.addAttribute("key", object)的方式,頁面通過各種表達式將其顯示出來;
ModelAndView有兩個作用,一個是上面Model的作用;另一個就是可以設置view,也就是跳轉方向,view既可以是字符串,也可以是View類型的object。

三、添加代碼到zoolist.html

1     <div th:text="${MSG}">這里是信息提示.</div>  
2         <br>  
3             <div th:errors="${animalForm.oname}"></div>  
4             <div th:errors="${animalForm.ocount}"></div>  
5             <div th:errors="${animalForm.memo}"></div>  

完整的內容:

    <!DOCTYPE html>  
    <html xmlns:th="http://www.thymeleaf.org">  
    <head>  
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
    <title>zoo list</title>  
    </head>  
    <body>  
    <a href='.'>首頁</a>->動物列表  
        <br><br>  
            <div th:text="${MSG}">這里是信息提示.</div>  
        <br>  
            <div th:errors="${animalForm.oname}"></div>  
            <div th:errors="${animalForm.ocount}"></div>  
            <div th:errors="${animalForm.memo}"></div>  
        <br>  
        <form id="iform" th:action="@{/list.html?save}" th:method="post" th:object="${animalForm}">  
            <table border="1">    
              <tr>   
                <th>動物名稱</th>    
                <th>數量</th>   
                <th>備注</th>  
                <th>Action</th>  
              </tr>    
              <tr>   
                <td><input type="text" name="oname" value="" th:value="*{oname}"/></td>  
                <td><input type="text" name="ocount" value="" th:value="*{ocount}"/></td>  
                <td><input type="text" name="memo" value="" th:value="*{memo}"/></td>  
                <td><input type="submit" value="添加"/></td>  
              </tr>  
            </table>  
        </form>  
        <hr>  
        <table border="1">    
          <tr>  
            <th>序號</th>  
            <th>動物名稱</th>    
            <th>數量</th>   
            <th>備注</th>  
          </tr>  
          <tr>  
            <td>1</td>  
            <td>大馬猴</td>  
            <td>10</td>  
            <td>機靈古怪,俏皮活潑</td>  
          </tr>  
          <tr>  
            <td>2</td>  
            <td>大熊貓</td>  
            <td>80</td>  
            <td>體型笨重,喜歡吃竹子</td>  
          </tr>  
          <tr>  
            <td>3</td>  
            <td>澳洲羊駝</td>  
            <td>13</td>  
            <td>長相奇特,大國人俗稱其草泥馬</td>  
          </tr>  
          <tr>  
            <td>4</td>  
            <td>峨眉山猴</td>  
            <td>90</td>  
            <td>不怕人,有時候發賤搶游客面包吃</td>  
          </tr>  
        </table>  
    </body>  
    </html>  

解釋:
<div th:text="${MSG}">這里是信息提示.</div>,還記得前面controller的代碼中我們在返回頁面的model中放了一個attribute名字叫“MSG”么,對了,在這里我們就可以通過表達式th:text="${MSG}"取得其值了。thymeleaf解析這個標簽的時候會將“這里是信息提示.”這個字符串替換成“MSG”對應的內容。

<div th:errors="${animalForm.oname}"></div>,thymeleaf使用th:errors表達式可以取得錯誤信息的內容,${animalForm.oname}表示取得animalForm里oname字段的錯誤信息;如果驗證oname字段時出現錯誤,那么在這個div里面會顯示出該錯誤信息。

關於thymeleaf的validate表達式詳細介紹請參閱 http://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#validation-and-error-messages

好了,我們該修改的都完成了,重啟tomcat進入瀏覽器吧,我的效果如下:
驗證有錯誤的情況:

驗證通過的效果:

好啦,如果你也做到這個樣子,也就算達到本篇的目的了,這個頁面雖然很簡陋,用的也都是靜態數據,但這基本上展示了如何使用@valid做form驗證。

關於驗證改進:
對於oname:我們可以換成@NotBlank注解,它可以將全是空格的字符串作為空字符串,和@NotEmpty不同的是,@NotBlank只可用於CharSequence類型,並檢查該元素是否為null或者該元素經過trim之后的長度是否為0。

對於ocount:我們把驗證代碼修改為:

    @NotBlank(message="數量: 不能為空")  
    @Pattern(regexp="[1-9]{1,3}", message="數量X: 必須為正整數,並且0<X<1000")  
    private String ocount; 

@Pattern,一看便知這是用正則表達式做檢查,[1-9]{1,3}表示三位正整數並且要大於0。
如果你對正則表達式還不了解,可以參閱javaSE的api文檔:http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html


對於一些特殊的驗證,你可以定義自己的驗證類,既可以實現springMVC的validator接口,也可以實現JSR303的注解約束方式驗證,這里采用JSR303方式。
1, 創建package:com.zoo.constraint,以及com.zoo.constraint.impl。
2, 在com.zoo.constraint中定義Annotation類型Memo:

 1     package com.zoo.constraint;  
 2       
 3     import static java.lang.annotation.ElementType.FIELD;  
 4     import static java.lang.annotation.ElementType.METHOD;  
 5     import static java.lang.annotation.RetentionPolicy.RUNTIME;  
 6       
 7     import java.lang.annotation.Retention;  
 8     import java.lang.annotation.Target;  
 9       
10     import javax.validation.Constraint;  
11     import javax.validation.Payload;  
12       
13     import com.zoo.constraint.impl.MemoValidator;  
14       
15     @Retention(RUNTIME)  
16     @Target({ FIELD, METHOD })  
17     @Constraint(validatedBy=MemoValidator.class)  
18     public @interface Memo {  
19           
20         String message() default "請輸入正確的備注";  
21           
22         Class<?>[] groups() default {};  
23           
24         Class<? extends Payload>[] payload() default {};  
25     }  

3, 在com.zoo.constraint.impl中定義類MemoValidator:

 1     package com.zoo.constraint.impl;  
 2       
 3     import java.util.HashSet;  
 4       
 5     import javax.validation.ConstraintValidator;  
 6     import javax.validation.ConstraintValidatorContext;  
 7       
 8     import com.zoo.constraint.Memo;  
 9       
10     public class MemoValidator implements ConstraintValidator<Memo, String> {  
11       
12         @Override  
13         public void initialize(Memo arg0) {  
14         }  
15       
16         @Override  
17         public boolean isValid(String arg0, ConstraintValidatorContext arg1) {  
18             HashSet<String> memoSet = new HashSet<String>();  
19             memoSet.add("圈養");  
20             memoSet.add("散養");  
21             return memoSet.contains(arg0);  
22         }  
23       
24     }  

4, 修改AnimalForm類:

 1     package com.zoo.web.form;  
 2       
 3     import javax.validation.constraints.Pattern;  
 4       
 5     import org.hibernate.validator.constraints.NotBlank;  
 6       
 7     import com.zoo.constraint.Memo;  
 8       
 9     public class AnimalForm {  
10       
11         private long id;  
12           
13         @NotBlank(message="動物名: 不能為空")   
14         private String oname;  
15           
16         @Pattern(regexp="[1-9]{1,3}", message="數量X不能為空,必須為正整數,並且0<X<1000")  
17         private String ocount;  
18           
19         @Memo(message = "備注不能為空,且只能填寫\"圈養\",或者\"散養\"")  
20         private String memo;  
21       
22         /** getters and setters **/  
23     }  

ok!重啟tomcat,進入瀏覽器,在三個字段都不輸入的情況下,我的界面如下:

 

 本篇就此結束,是不是很簡單呢,這里只是拋磚引玉做一個極簡的介紹,如需深入了解請參閱官方文檔。

下一篇是數據持久化之保存在MySQL數據庫。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM