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
      
图10-3 用户注册功验证错误1
(2)       当在页面上输入用户名为11,密码为11,年龄为-11,邮箱为11时:
     
图10-4 用户注册功验证错误2
(3)          当在页面上输入用户名为唐琳,密码为 123456,年龄为 33,邮箱为 tanglin@dlut.edu.cn时:
     
图10-5 用户注册功验证成功
         以下为【用户登陆功能】、【用户注册功能】实现的代码结构:
图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>