Spring验证的错误返回------BindingResult
最近做的一个项目使用了 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:input path="email"
size="30" onblur="checkEmail();"/>
//path的值必须和accountVo这个对象的属性一致
<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>
//如果是英文浏览器, "邮箱格式不正确!"就会使英文版本的。
加载中,请稍候......