最近做的一個項目使用了 Spring MVC3, 其中驗證也是使用Spring的 validate 框架, 但不是全部。
我們只是使用了org.springframework.validation.Errors, org.springframework.validation.BindingResult 來將驗證錯誤信息返回到JSP頁面。因為Spring提供了<form:errors>標簽來顯示BindingResult對象里的錯誤信息, 并且這個驗證框架還支持國際化, errorCode對應的語言文字放到工程的message資源文件就好了。
下面是一個簡單的注冊賬戶的例子:包括三個文件:JSP, AccountValidator和AccountValidator。
1. ###首先是JSP頁面:addAccount,jsp的表單
<form:form modelAttribute="accountVo" action="${actionUrl}" method="post">
<form:hidden path="id" readonly=“readonly”/>
<form:errors path="email" cssClass="errorMsg"></form:errors>
//這里省略了表單的其他元素, 直接來提交按鈕
<input type="button" id="saveAccount" value='<fmt:message key="button.next" />' onclick="submitAccount ('accountVo')"/>
//這里完全可以使用type=“submit”, 這里使用button可以截獲提交事件, 并在提交之前先做JS層面的驗證
</form:form>
Note: 使用上面這些標簽, 必須引入Spring 的 form標簽庫:<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
大家還看到我們使用了JSTL的fmt標簽庫(國際化), 這個也要引入<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>, 其實我們也完全可以用<form message>標簽來做國際化的。這里我還想說一個東西:readonly這個屬性根本起不到只讀的作用, 完全可以被修改,但是使用disable屬性后,這個表單元素就無法放到accountVo這個對象并提交了, 糾結!
2. ###這里是個不完整的驗證類AccountValidator, 注意,我們沒有實現Validator接口
public class AccountValidator {
public void validate(AccountVo accountVo, Errors errors) {
String email = accountVo.getEmail();
if (!StringUtils.hasLength(email)) {
errors.rejectValue("email", "validate.email.empty", "郵箱不能為空");//這個函數有好幾個重載的變體
}
}
}
Note: Errors這個接口有好幾個rejectValue()函數, 它們是可以支持國際化的。 比如, 上面這個例子表示, 錯誤的字段(filed)是“email”, errorCode是“validate.email.empty”, 與資源文件對應, 第三個是defaultMessage。很多國際化當中會帶有參數, rejectValue其中的一個重載函數就是rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage)。
3. ###最后是AccountController
@Controller//基于注解, 聲明這是一個controller
@RequestMapping(value="/account") //表示總的路徑
@SessionAttributes("account") //表示account對象將會存入session當中, //默認情況下model.addAttribute(account)將會把account對象放入request當中, 并且屬性名為“account”
public class AccountFormController {
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String addAccount(@ModelAttribute("accountVo") AccountVo accountVo,
BindingResult result, //這里面,BindResult result必須緊跟著前面的@ModelAttribute, 否則會出錯
HttpServletResponse response,
HttpSession session, Model model) {
log.debug(accountVo.toString());
if(isExist(accountVo)){
log.debug("Opps, 這個email已經注冊過了!");
result.rejectValue("email", "misFormat", "這個email已經注冊過了!");
return "account/addAccount";
}else{
new AccountValidator().validate(accountVo, result);
if(result.hasErrors()){
log.debug("表單數據有誤, 重新填寫"+accountVo);
model.addAttribute("accountVo",accountVo);//把accountVo對象返回到頁面, 這樣不至于表單被清空了
return "account/addAccount";//返回到注冊頁面, 同時, 這里會自動將驗證錯誤信息返回到JSP頁面, 怎么返回呢?看后面!
}
//這里會做很多數據庫的操作, 省略
}
} //end of login()
}//end of controller
Note: 這里需要特別注意幾個問題:1. 函數形參 BindResult result 必須緊跟著前面的@ModelAttribute, 否則會出異常; 2. @ModelAttribute("accountVo") AccountVo accountVo, 這個參數與JSP頁面的<form:form modelAttribute="accountVo" action="${actionUrl}" method="post">對應
4. ###進階一下, 看看驗證錯誤信息對象是怎么傳遞到頁面的
這一切看起來都很完美, 但是有時候出于設計的原因, 我們不得不使用redirect, 對, 就是重定向! 就是這個東西讓我對Spring MVC有了一點不好的印象, 特別是結合了sitemesh之后。這個先打住, 咱們還是說驗證錯誤怎么傳給重定向之后的JSP頁面吧。
其實也簡單, 咱們可以先把錯誤對象放入session當中, 然后在另一個Controller里把它取出來, 然后再返回到相應的JSP頁面就行了!
對!但是,這里要注意了, BindingResult這個對象是自動傳入JSP的, 我們不知道應該把它放在request里面呢還是session里面, 或者其他的地方, 以及屬性名叫什么。這個就是我昨天晚上糾結的問題, 最后看了一下Spring 的源代碼, 終于稍微清楚了一點兒。下面直接上代碼,然后解釋。
if(session.getAttribute("BindingResult.accountVo") != null){
//放到session和request里面, 不論attr name設置成什么都不行
//只有這樣才能把bindingresult的錯誤信息傳到JSP頁面
String errorAttrName = "org.springframework.validation.BindingResult.accountenterpriseVo";
model.addAttribute(errorAttrName, session.getAttribute("BindingResult.accountVo"));
session.removeAttribute("BindingResult.enterpriseVo");
}
首先, 驗證錯誤對象 BindingResult 必須放入 org.springframework.ui.Model 當中返回給JSP頁面。放到request和session當中都沒用。
第二,這個屬性名是BindingResult.getClass().getName + “.” + targetName, 也就是上面那一長串, 其中targerName對應著JSP頁面的表單的modelAttribute, 即<form:form modelAttribute="accountVo" action="${actionUrl}" method="post">中的accountVo。 對了, 順便說一下, accountVo同時還是表單的id, 大家可以用firefox的firebug查看頁面元素。<form:error>標簽也會被翻譯成
<span class="errorMsg" id="email.errors">郵箱格式不正確!</span> //如果是英文瀏覽器, “郵箱格式不正確!”就會使英文版本的。
好的, 就先寫到這里了。