输入验证与数据绑定 实例目标:实现用户注册功能。 流程: 1. 提供一个界面供用户输入注册信息,下面是一个简化的注册界面,仅提供了用 户名和密码的设置 2. 如果用户注册信息有误,显示错误界面,要求用户检查输入后重新注册。 3. 注册成功,显示操作成功提示。 实例内容 <!--[if !supportLists]-->a) <!--[endif]-->配置文件 首先,web.xml文件配置分发器如下: <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java./xml/ns/j2ee" xmlns:xsi="http://www./2001/XMLSchema-instance" xsi:schemaLocation="http://java./xml/ns/j2ee http://java./xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>Dispatcher</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/Config.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>Dispatcher</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <error-page> <exception-type>java.lang.Throwable</exception-type> <location>/WEB-INF/jsp/errorpage.jsp</location> </error-page> <error-page> <exception-type>500</exception-type> <location>/WEB-INF/jsp/errorpage.jsp</location> </error-page> </web-app> 在这个实例中,我们选用JSTLView作为我们的表现层实现。对应的配置文件如下。 Config.xml: <beans> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalRes ourceViewResolver"> <property name="viewClass"> <value> org.springframework.web.servlet.view.JstlView </value> </property> <property name="prefix"> <value>/WEB-INF/view/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> <bean id="RegisterValidator" ⑴ class="net.xiaxin.validator.RegisterValidator"/> <bean id="RegisterAction" class="net.xiaxin.action.RegisterAction"> <property name="commandClass"> <value>net.xiaxin.reqbean.RegisterInfo</value> </property> <property name="validator"> ⑵ <ref local="RegisterValidator"/> </property> <property name="formView"> ⑶ <value>register</value> </property> <property name="successView"> ⑷ <value>RegisterSuccess</value> </property> </bean> <!--Request Mapping --> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUr lHandlerMapping"> <property name="mappings"> <props> <prop key="/register.do">RegisterAction</prop> </props> </property> </bean> </beans> 这个配置文件与篇首MVC介绍中所用实例大同小异。不同之处在于我们在这里引入了数 据验证配置节点: ⑴ 配置了一个数据验证Bean: RegisterValidator net.xiaxin.validator.RegisterValidator ⑵ 为逻辑处理单元RegisterAction定义输入数据校验Bean 这里通过一个Bean引用,将RegisterValidator配置为本Action的数据校验 类。 ⑶ 指定本处理单元的显示界面。 formView是RegisterAction的父类SimpleFormController中定义的属 性,指定了本处理单元的显示界面。 这里即用户访问register.do时将显示的注册界面。 要注意的是,完成此界面后,我们必须通过“…/register.do”访问注册界面, 而不是“…/register.jsp”,因为我们必须首先借助Spring完成一系列初始 化工作(如创建对应的状态对象并与之关联)之后,register.jsp才能顺利执 行,否则我们会得到一个应用服务器内部错误。 ⑷ 指定成功返回界面。 successView同样是RegisterAction的父类SimpleFormController中定 义的属性,它指向成功返回界面。 b) 数据验证类 在Spring中,所有的数据验证类都必须实现接口: org.springframework.validation.Validator Validator接口定义了两个方法: boolean supports(Class clazz); 用于检查当前输入的数据类型是否符合本类的检验范围。Spring调用 Validator实现类时,首先会通过这个方法检查数据类型是_____否与此Validator 相匹配。 void validate(Object obj, Errors errors); 数据校验方法。Validator实现类通过实现这个方法,完成具体的数据校验逻辑。 RegisterValidator.java: public class RegisterValidator implements Validator { public boolean supports(Class clazz) { return RegisterInfo.class.isAssignableFrom(clazz); ⑴ } public void validate(Object obj, Errors errors) { RegisterInfo regInfo = (RegisterInfo) obj; ⑵ //检查注册用户名是否合法 if (regInfo.getUsername().length() < 4) { errors.rejectValue("username", ⑶ "less4chars", null, "用户名长度必须大于等于4个字母!"); } /*检查用户名是否已经存在 if (UserDAO.getUser(regInfo.getUsername()) != null) { errors.rejectValue("username", "existed", null, "用户已存在!"); } */ if (regInfo.getPassword1().length() < 6) { errors.rejectValue("password1", "less6chars", null, "密码长度必须大于等于6个字母"); } if (!regInfo.getPassword2().equals(regInfo.getPassword1())) { errors.rejectValue("password2", "notsame", null, "两次输入的密码不一致!"); } } } ⑴ RegisterInfo.class.isAssignableFrom方法用于判定参数类别,当传入 Class对象与当前类类别相同,或是当前类的父类(或当前类实现的接口)时返回真。这 里我们将其用于对校验对象的数据类型进行判定(这里的判定条件为:校验对象必须是 RegisterInfo类的实例)。 ⑵ RegisterInfo regInfo = (RegisterInfo) obj; 将输入的数据对象转换为我们预定的数据类型。 ⑶ 通过rejectValue方法将错误信息加入Error列表,此错误信息将被页面捕获并 显示在错误提示界面上。 rejectVlaue方法有4个参数: 1.Error Code 显示错误时,将根据错误代码识别错误信息类型。 2.Message Key 上 面关于ApplicationContext 的国际化支持时, 我们曾经谈及 MessageSource的使用,这里我们可以通过引入MessageSource实现提示信息 的参数化,此时,本参数将用作.properties文件中的消息索引。 3.Error Argument 如果提示信息中需要包含动态信息,则可通过此参数传递需要的动态信息对象。具 体参见ApplicationContext中关于国际化实现的描述。 4.Default Message 如果在当前MessageSource中没有发现Message Key对应的信息数据,则以此 默认值返回。 这里我们暂时尚未考虑国际化支持,所有的信息都将通过Default Message返 回。关于国际化支持请参见稍后章节。 另外rejectValue还有另外几个简化版本,可根据情况选用。 其中RegisterInfo类定义如下: public class RegisterInfo { private String username; private String password1; private String password2; public String getPassword1() { return password1; } public void setPassword1(String password1) { this.password1 = password1; } public String getPassword2() { return password2; } public void setPassword2(String password2) { this.password2 = password2; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } } c) 注册界面 register.jsp提供了注册操作界面。它同时提供了最初的注册界面,当输入参数非 法时,同时也会显示错误信息,提示用户检查输入。 register.jsp: <!-- 页面中使用了JSTL Core taglib 和Spring lib--> <%@ taglib prefix="c" uri="http://java./jstl/core_rt" %> <%@ taglib prefix="spring" uri="http://www./tags" %> <!-- 设定页面编译时采用gb2312编码,同时指定浏览器显示时采取gb2312解码--> <%@ page pageEncoding="gb2312" contentType="text/html;charset=gb2312"%> <html> <head> <title>用户注册</title> </head> <body style="text-align: center"> <form method="POST" action="/register.do"> <spring:bind path="command.*"> <font color="#FF0000"> <c:forEach items="${status.errorMessages}" var="error"> 错误: <c:out value="${error}"/><br> </c:forEach> </font> </spring:bind> <table border="0" width="450" height="101" cellspacing="0" cellpadding="0" > <tr> <td height="27" width="408" colspan="2"> <p align="center"><b>用户注册</b></td> </tr> <tr> <td height="23" width="104">用户名:</td> <td height="23" width="450"> <spring:bind path="command.username"> <input type="text" value="<c:out value="${status.value}"/>" name="<c:out value="${status.expression}"/>" > (必须大于等于4个字符) <br> <c:if test="${status.error}"> <font color="#FF0000"> 错误: <c:forEach items="${status.errorMessages}" var="error"> <c:out value="${error}"/> </c:forEach> </font> </c:if> </spring:bind> </td> </td> </tr> <tr> <td height="23" width="104">密码:</td> <td height="23" width="450"> <spring:bind path="command.password1"> <input type="password" value="<c:out value="${status.value}"/>" name="<c:out value="${status.expression}"/>" > (必须大于等于6个字符) <br> <c:if test="${status.error}"> <font color="#FF0000"> 错误: <c:forEach items="${status.errorMessages}" var="error"> <c:out value="${error}"/> </c:forEach> </font> </c:if> </spring:bind> </td> </tr> <tr> <td height="23" width="104">重复密码:</td> <td height="23" width="450"> <spring:bind path="command.password2"> <input type="password" value="<c:out value="${status.value}"/>" name="<c:out value="${status.expression}"/>" > <br> <c:if test="${status.error}"> <font color="#FF0000"> 错误: <c:forEach items="${status.errorMessages}" var="error"> <c:out value="${error}"/> </c:forEach> </font> </c:if> </spring:bind> </td> </tr> </table> <p> <input type="submit" value="提交" name="B1"> <input type="reset" value="重置" name="B2"> </p> </form> </body> </html> 页面起始部分指定了页面中引入的taglib和页面编码方式,实际开发时应该将其独立到一个单 独的jsp文件中,并在各个jsp文件中include便于统一维护。 页面中关键所在,也就是<spring:bind> 标记的使用: <spring:bind path="command.*"> <font color="#FF0000"> <c:forEach items="${status.errorMessages}" var="error"> 错误: <c:out value="${error}"/><br> </c:forEach> </font> </spring:bind> spring.bind标记通过path参数与CommandClass对象相绑定。之后我们就可以对绑定的 CommandClass对象的状态信息进行访问。上面的片断中,我们通过通配符“*”将当前 spring.bind语义与command对象的所有属性相绑定,用于集中的错误信息显示,对应最终提 示界面中的(蓝框标注部分): 而在下面每个输入框下方,我们也提供了对应的错误提示,此时我们绑定到了特定的command 属性,如"command.username"。 这里的"command"是Spring中的默认CommandClass名称,用于引用当前页面对应的 CommandClass实例(当前语境下,也就是net.xiaxin.reqbean.RegisterInfo)。我们 也可以配置CommandClass引用名称,在Config.xml中RegisterAction配置中增加 CommandName配置,如下: <bean id="RegisterAction" class="net.xiaxin.action.RegisterAction"> <property name="commandName"> <value>RegisterInfo</value> </property> ……… </bean> 之后我们就可以在页面中使用“RegisterInfo”替代现在的“command”对数据对象进 行引用。 (为了保持前后一致,下面我们仍旧以“command”为例) 绑定到username属性的<spring:bind>标记: <spring:bind path="command.username"> <input type="text" value="<c:out value="${status.value}"/>" name="<c:out value="${status.expression}"/>" > (必须大于等于4个字符) <br> <c:if test="${status.error}"> <font color="#FF0000"> 错误: <c:forEach items="${status.errorMessages}" var="error"> <c:out value="${error}"/> </c:forEach> </font> </c:if> </spring:bind> 可以看到,<spring:bind>语义内,可以通过${status.*}访问对应的状态属性。 ${status.*} 对应的实际是类 org.springframework.web.servlet.support.BindStatus BindStatus类提供了与当前CommandClass对象绑定的状态信息,如: ${status.errorMessages}对应绑定对象属性的错误信息。 ${status.expression}对应绑定对象属性的名称。 ${status.value}对应绑定对象属性当前值。 具体描述可参见BindStatus类的Java Doc 文档。 下面是RegisterAction.java和成功返回界面RegisterSuccess.jsp,出于演示目的,这 两个文件都非常简单: RegisterAction.java: public class RegisterAction extends SimpleFormController { protected ModelAndView onSubmit(Object cmd, BindException ex) throws Exception { Map rsMap = new HashMap(); rsMap.put("logininfo",cmd); return new ModelAndView(this.getSuccessView(),rsMap); } } RegisterSuccess.jsp: <%@ taglib prefix="c" uri="http://java./jstl/core_rt" %> <%@ page pageEncoding="gb2312" contentType="text/html;charset=gb2312"%> <html> <body> <p align="center"> <c:out value="${logininfo.username}"/> 注册成功! </p> </body> </html> 可以看到,结合JSTL Core Taglib和Spring Taglib,我们实现了一个拥有数据校验 功能的注册界面。界面显示、数据校验、逻辑处理三大模块被清晰隔离互不干扰,相对传统的 jsp解决方案。系统的可维护性得到了大大提升。 不过,我们还必须注意到,spring:bind标记对界面代码的侵入性较大,可以看到页面中 混杂了大量的Tag调用,这将对界面的修改和维护带来一定的困难。相对WebWork2而言, Spring在这方面还是显得有些繁琐。 |
|
来自: BlazerOfIT > 《spring》