1. 程式人生 > >SpringMVC4+thymeleaf3的一個簡單例項(篇四:form表單資料驗證)

SpringMVC4+thymeleaf3的一個簡單例項(篇四:form表單資料驗證)

關於表單資料驗證有很多中方法,這裡我僅介紹JSR303註解驗證。

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

JSR303僅僅是一個規範,這裡我們要用到它的一個實現:hibernate-validator。

開工之前,我們需要引入以下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欄位上分別加上驗證註解,程式碼如下:

package com.zoo.web.form;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range;

public class AnimalForm {

	private long id;
	
	@NotEmpty(message="動物名: 不能為空") 
	private String oname;
	
	@Range(min = 1, message="數量: 必須大於0")
	@NotNull(message="數量: 不能為空")
	private int ocount;
	
	@Size(max = 10, message="備註: 長度不能超過10個字元")
	private String memo;

	/** 省略getter和setter **/

	
}
解釋:

@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,這不是我們所希望的,具體的改進程式碼我們在篇末說明。

二、修改ZooController裡的doAdd方法:

@RequestMapping(path = "/list", params = {"save"}, method = RequestMethod.POST)
	public String doAdd(Model model, @Valid AnimalForm form, BindingResult result){
		System.out.println("動物名:" + form.getOname());
		System.out.println("數量:" + form.getOcount());
		System.out.println("備註:" + form.getMemo());
		if(result.hasErrors()){
			model.addAttribute("MSG", "出錯啦!");
		}else{
			model.addAttribute("MSG", "提交成功!");
		}
		return "zoolist";
	}
解釋:

方法中Model引數,用於存放任意資料以便傳遞到頁面,注意Model僅僅是一個介面,spring框架會幫我們例項化具體的類並設定到該方法當中;上例我們在該model裡放了一個key為“MSG”的attribute,頁面上通過表示式就可以取得其值。

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

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

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

(1)

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

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

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

這裡說一下Model和ModelAndView的區別

Model主要用於將資料傳遞到頁面,一般採用model.addAttribute("key", object)的方式,頁面通過各種表示式將其顯示出來;

ModelAndView有兩個作用,一個是上面Model的作用;另一個就是可以設定view,也就是跳轉方向,view既可以是字串,也可以是View型別的object。

三、新增程式碼到zoolist.html

<div th:text="${MSG}">這裡是資訊提示.</div>
	<br>
		<div th:errors="${animalForm.oname}"></div>
		<div th:errors="${animalForm.ocount}"></div>
		<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裡面會顯示出該錯誤資訊。

好了,我們該修改的都完成了,重啟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。

對於一些特殊的驗證,你可以定義自己的驗證類,既可以實現springMVC的validator介面,也可以實現JSR303的註解約束方式驗證,這裡採用JSR303方式。

1, 建立package:com.zoo.constraint,以及com.zoo.constraint.impl。

2, 在com.zoo.constraint中定義Annotation型別Memo:

package com.zoo.constraint;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

import com.zoo.constraint.impl.MemoValidator;

@Retention(RUNTIME)
@Target({ FIELD, METHOD })
@Constraint(validatedBy=MemoValidator.class)
public @interface Memo {
	
	String message() default "請輸入正確的備註";
	
	Class<?>[] groups() default {};
	
	Class<? extends Payload>[] payload() default {};
}

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

import java.util.HashSet;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import com.zoo.constraint.Memo;

public class MemoValidator implements ConstraintValidator<Memo, String> {

	@Override
	public void initialize(Memo arg0) {
	}

	@Override
	public boolean isValid(String arg0, ConstraintValidatorContext arg1) {
		HashSet<String> memoSet = new HashSet<String>();
		memoSet.add("圈養");
		memoSet.add("散養");
		return memoSet.contains(arg0);
	}

}

4, 修改AnimalForm類:
package com.zoo.web.form;

import javax.validation.constraints.Pattern;

import org.hibernate.validator.constraints.NotBlank;

import com.zoo.constraint.Memo;

public class AnimalForm {

	private long id;
	
	@NotBlank(message="動物名: 不能為空") 
	private String oname;
	
	@Pattern(regexp="[1-9]{1,3}", message="數量X不能為空,必須為正整數,並且0<X<1000")
	private String ocount;
	
	@Memo(message = "備註不能為空,且只能填寫\"圈養\",或者\"散養\"")
	private String memo;

	/** getters and setters **/
}

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


是不是很簡單呢,不過希望大家在遇到問題時學會自己看reference和api,很多時候你遇到的問題都可以在那裡找到。

下一篇是資料持久化之儲存在MySql資料庫。