10.2.2 常用的用户输入验证实现方式

         10.2.1中虽然实现了用户登陆功能的,用户输入验证。但是这种方式却很少被使用。本节中介绍实际应用中较为常见的编码方式。代码实现进需要修改Action类,其余文件的内容无需修改。
代码10-1中登陆案例代码编写中将用户输入验证的代码实现放到execute方法中,这种方法虽然容易被读者理解。但是从代码设计角度来看存在execute方法过于臃肿、不利于代码的重用等问题。更为常见的代码实现方式,则是重写ActionSupport类中的validate方法,在该方法中完成用户输入验证。具体代码如下所示:
packagecom.study.erp.action;
importcom.opensymphony.xwork2.ActionSupport;
public class LoginAction extendsActionSupport{
    private String username;
    private String password;
    //此处省略属性对应的getset方法
    ….
    @Override
    public void validate() {
        if(username == null || username.trim().equals("")){
            addFieldError("username", "用户名不能为空.");
        }
        if(password == null || password.trim().equals("")){
            addFieldError("password", "密码不能为空.");         
        }else if(password.length()<6 || password.length()>12){
            addFieldError("password", "密码长度应在612位之间.");  
        }
    }
    public String execute(){
            return "success";
    }
}
     代码10-4用户登陆功能LoginAction
比较上述10-4代码和10-1代码读者可以发现,10-4的内容更加简洁。当进行用户输入验证时,该验证代码编写到validate方法。该方法中如果发现用户输入错误时,将错误信息添加到框架FieldError中即可。在进行完所有的用户输入验证后,无需用户编码实现页面的跳转。框架会验证用户输入验证是否正确,如果正确会继续执行execute方法,如果错误会跳转到该Action对应的名为input的结果视图中。
本节案例中的代码实现,是Struts框架下基于编码方式实现用户输入验证所提倡的方式。读者在编写时常见的错误是没有在配置文件中配置名为input的结果视图,其配置文件的代码为:<result name="input">/xxx.jsp</result>

10.2.3 多方法Action的用户输入验证实现方式

         在学习第二章Action的编写和配置时,我们已经了解到Struts2Action类中可以包含多个方法,每个方法用来完成不同的逻辑操作。例如,我们可以利用同一个Action来完成一组逻辑相关的操作:用户登陆、用户注册、用户信息修改和用户信息删除。代码实现时需要在一个Action类中需要包括4个方法,但是对于这4个方法来说用户输入校验的规则也是不同的,如果再使用10.2.2所讲的validate方法来实现用户输入验证,很难实现预期的效果。
Struts2框架对于这类情况也提供了相应的解决方案。当Action中包括多个逻辑操作方法时,为完成用户输入验证Action类的编码须实现Validateable接口(ActionSupport类已经实现了Validateable接口)并编写validateXxx方法,Xxx即对应Action逻辑操作方法的名字。Struts2框架利用了反射机制在对应的逻辑操作方法执行之前调用相应的validateXxx方法。
为了让读者更容易读懂本节中的代码,本案例中仅实现了用户注册和用户登陆功能的用户输入校验。用户登陆的用户输入验证规则不变,用户注册功能的用户输入验证规则包括:用户名不能为空、密码不能为空、密码长度必须在612位之间、年龄的输入范围必须在0-100之间,邮箱必须包括@字符。
用户登陆功能图示与图10-110-210-3一致,以下为用户注册功能截图:
(1)      当在页面上只输入年龄-11,邮箱为11
      Struts2用户输入验证(2)_实际应用Struts2用户输入验证(2)_的_02
图10-3 用户注册功验证错误1
(2)      当在页面上输入用户名为11,密码为11,年龄为-11,邮箱为11时:
      Struts2用户输入验证(2)_实际应用_03Struts2用户输入验证(2)_实际应用_04
图10-4 用户注册功验证错误2
(3)         当在页面上输入用户名为唐琳,密码为123456,年龄为33,邮箱为tanglin@dlut.edu.cn时:
      Struts2用户输入验证(2)_实际应用_05Struts2用户输入验证(2)_实际应用_06
图10-5 用户注册功验证成功
         以下为【用户登陆功能】、【用户注册功能】实现的代码结构:
Struts2用户输入验证(2)_用户_07
图10-6 用户登陆、用户注册功验证代码结构
编写该功能时用户需要编写和创建的文件:
文件名
说明
备注
Login.jsp
用户登陆页面
视图
LoginSuccess.jsp
用户登陆成功页面
视图
Regist.jsp
用户注册页面
视图
RegistSuccess.jsp
用户注册成功页面
视图
UserAction
Action
控制器
struts.xml
Struts2框架的配置文件
配置文件
web.xml
项目的部署描述文件
配置文件
由于篇幅的关系,以下仅列出UserAction类,Regist.jspstruts.xml文件的部分代码。其它代码参考光盘中Unit10文件夹下03的项目代码。
         UserAction类包含两个逻辑,因此包含了两个逻辑方法分别是loginregistlogin方法用于实现用户登陆功能,对应的用户输入校验方法为validateLogin方法;regist方法用于实现用户注册功能,对应的用户输入校验方法为validateRegist方法。具体代码如下所示:
packagecom.study.erp.action;
importcom.opensymphony.xwork2.ActionSupport;
public class UserActionextends ActionSupport{
    private String username;
    private String password;
    private int age;
    private String email;
    //此处省略属性对应的getset方法
    ….
    //用户登陆功能对应的用户输入校验方法
    public void validateLogin() {
        if(username == null || username.trim().equals("")){
            addFieldError("username","用户名不能为空.");
        }
        if(password == null || password.trim().equals("")){
            addFieldError("password", "密码不能为空.");          
        }else if(password.length()<6 || password.length()>12){
            addFieldError("password", "密码长度应在612位之间.");
        }
    }
//用户注册功能对应的用户输入校验方法
    public void validateRegist() {
        if(username == null || username.trim().equals("")){
            addFieldError("username", "用户名不能为空.");
        }
        if(password == null || password.trim().equals("")){
            addFieldError("password", "密码不能为空.");          
        }else if(password.length()<6 || password.length()>12){
            addFieldError("password", "密码长度应在612位之间.");
        }
        if(age <0 || age>100){
            addFieldError("age", "年龄不符合要求."); 
        }
        if(email.indexOf("@") == -1){
            addFieldError("email", "Email格式不符合要求.");  
        }
    }
 
    public String login(){
            return "success";
    }
   
    public String regist(){
            return "success";
    }
}
         以上代码中用户输入校验方法名编写时务必注意:  1.validateXxx方法,方法名Xxx为对应的逻辑方法方法名,首字母大写。2. 方法的权限为public,方法没有参数,返回值为void类型。
         struts.xml文件中当前的Action类对应的每个action配置都需要包含名为“input”的结果视图,具体的配置信息如下所示:
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTDStruts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
     <package name="default"namespace="/" extends="struts-default">
        <action name="login"class="com.study.erp.action.UserAction" method="login">
            <result name="input">/Login.jsp</result>
            <result name="success">/LoginSuccess.jsp</result>
        </action>
        <action name="regist"class="com.study.erp.action.UserAction"  method="regist">
            <result name="input">/Regist.jsp</result>
            <result name="success">/RegistSuccess.jsp</result>
        </action>
    </package>
</struts>
    用户注册视图Regist.jsp代码如下所示:
<%@ pagelanguage="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@ tagliburi="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>用户注册页面</title>
</head>
<body>
    <h1>用户注册</h1>
    <hr>
    <s:form action="regist.action">
        <s:textfield name="username" label="用户名"></s:textfield>
        <s:password name="password" label="密码"></s:password>
        <s:textfield name="age" label="年龄"></s:textfield>
        <s:textfield name="email" label="电子邮件:"></s:textfield>
        <s:submit name="login" value="注册"/>
    </s:form>
</body>
</html>
    以上为实现登陆和注册功能的主要代码,我们仔细分析发现validateLogin()validateRegist()代码中部分代码是重复的,但是上面的代码中由于用户输入验证对应的方法是不同的,所以代码出现了重复的现象,虽然我们可以单独把这段重复的代码单独写到一个方法中,然后在这两个用户输入验证方法中进行调用以进行改进。
Struts2框架的validate方法是由DefaultWorkFlowInterceptor拦截器调用的。该拦截器包含在defaultStack拦截器栈中。因此,只要使用默认的拦截器栈该拦截器都回被运行,从而validate方法会被调用。换句话说,默认情况下当前Action的所有逻辑方法执行之前都会执行validate方法。如果validate方法和validateXxx方法同时存在,它们的执行顺序是先执行validateXxx方法,再执行validate方法。针对于Struts框架中本例中的Action类可以做如下优化:
packagecom.study.erp.action;
importcom.opensymphony.xwork2.ActionSupport;
public class UserActionextends ActionSupport{
    private String username;
    private String password;
    private int age;
    private String email;
    //此处省略属性对应的getset方法
    ….
    //用户登陆功能对应的用户输入校验方法,此时本方法可以省略不写
    public void validateLogin() {
    }
    //用户注册功能对应的用户输入校验方法
    public void validateRegist() {
        if(age <0 || age>100){
            addFieldError("age", "年龄不符合要求.");
        }
        if(email.indexOf("@") == -1){
            addFieldError("email", "Email格式不符合要求."); 
        }
    }
    //用户输入验证方法
    public void validate(){
        if(username == null || username.trim().equals("")){
            addFieldError("username", "用户名不能为空.");
        }
        if(password == null || password.trim().equals("")){
            addFieldError("password", "密码不能为空.");         
        }else if(password.length()<6 || password.length()>12){
            addFieldError("password", "密码长度应在612位之间.");  
        }
    }
    public String login(){
            return "success";
    }
    public String regist(){
            return "success";
    }
}
    如果某action中包含了多个逻辑方法,其中绝大部分方法都有相同的验证规则,只有个别方法不需要该验证规则时。可以使用DefaultWorkFlowInterceptor拦截器的excludeMethods参数,指定被排除的方法。如果被排除的方法超过1个,方法名中间使用逗号进行间隔。
    例如UserAction类中包含了3个方法:用户登陆login方法,用户注册regist方法,用户信息删除的delete方法。而用户信息删除不需要执行validate方法用户名和用户密码的验证规则。具体的实现方法可以包括以下两种,比较而言第二种方法更容易理解也更加简单。
1.重新设定默认的拦截器栈,在该拦截器栈中将delete方法排除掉。对应的struts.xml文件的内容如下所示:
<struts>
     <package name="default"namespace="/" extends="struts-default">
         <interceptors>
             <interceptor-stackname="mydefaultStack">
                 <interceptor-refname="defaultStack">
                  <paramname="workflow.excludeMethods">delete</param>
              </interceptor-ref>
             </interceptor-stack>
         </interceptors>
         <default-interceptor-refname="mydefaultStack"></default-interceptor-ref>
        <action name="login"class="com.study.erp.action.UserAction" method="login">
            <result name="input">/Login.jsp</result>
            <result name="success">/LoginSuccess.jsp</result>
        </action>
        <action name="regist"class="com.study.erp.action.UserAction"  method="regist">
            <result name="input">/Regist.jsp</result>
            <result name="success">/RegistSuccess.jsp</result>
        </action>
        <action name="delete"class="com.study.erp.action.UserAction"  method="delete">
            <result name="success">/DeleteSuccess.jsp</result>
        </action>
    </package>
</struts>
    2.只在删除用户信息对应的action配置信息中将delete方法从拦截器中删除掉。对应的配置文件内容
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTDStruts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
     <package name="default"namespace="/" extends="struts-default">
        <action name="login"class="com.study.erp.action.UserAction" method="login">
            <result name="input">/Login.jsp</result>
            <result name="success">/LoginSuccess.jsp</result>
        </action>
        <action name="regist"class="com.study.erp.action.UserAction"  method="regist">
            <result name="input">/Regist.jsp</result>
            <result name="success">/RegistSuccess.jsp</result>
        </action>
        <action name="delete"class="com.study.erp.action.UserAction"  method="delete">
            <result name="success">/DeleteSuccess.jsp</result>
            <interceptor-ref name="defaultStack">
                  <paramname="workflow.excludeMethods">delete</param>
            </interceptor-ref>
        </action>
    </package>
</struts>