Struts2讲义34(转载)

8  Struts2 输入校验

1 章记述的类型转换异常处理情况其实就笔者认为也可以算是 Struts2 的输入校验中的 1 种方式。在记述 Struts2 的核心技术时候,笔者也简单介绍过一些输入校验的 Struts2 的自带类和方法。现在在本章重新整理一下,将 Struts2 中的所有输入校验的使用做个完整而又详细的介绍。

8.1   validate 输入校验方式再谈

Web 系统项目中有大量的视图页面需要用户自行输入很多数据。这些数据的类型有很多种。为了防止某些客户的恶意输入以及对 Web 项目的恶意破坏。必须引入输入校验像 Windows 操作系统中的防火墙一样把一些“垃圾”数据过滤,挡在 Web 系统之外。

Struts2 的输入校验是以上一章的类型转换为基础。而且输入校验一般和 Web 系统的业务逻辑息息相关。所以在阅读本章前,笔者建议读者能仔细参看类型转换章节。有了坚实的基础再来阅读本章。

在前面的章节中笔者也对 Struts2 输入校验中最基本的使用 validate 方式做过简单介绍,本小节再次就这一话题进行讨论。

8.1.1   复习 validate 方法进行输入校验

技术要点

本节代码就 1 个简单的用户注册功能具体介绍利用 validate 方法对数字、字符串、日期等类型数据进行输入校验方式介绍。

几种基本 Java 数据类型输入校验。

针对具体业务逻辑进行输入校验。

演示代码

使用的 Action 文件:

 

Java代码 Struts2讲义34(转载)
  1. <!------------ 文件名: RegisterAction.java---------------->   
  2. public class RegisterAction extends ActionSupport {   
  3.          // Action 类公用私有变量,用来做页面导航标志   
  4.          private static String FORWARD = null ;   
  5.   
  6.          // 用户名属性   
  7.          private String username;   
  8. ………   
  9.          // 年龄属性   
  10.          private int age;   
  11.   
  12.          // 取得用户名值   
  13.          public String getUsername() {   
  14.                    return username;   
  15.          }   
  16.     
  17.          // 设置用户名值   
  18.          public void setUsername(String username) {   
  19.                    this .username = username;   
  20.          }   
  21.   
  22. …………………   
  23.   
  24.          // 取得年龄值   
  25.          public int getAge() {   
  26.                    return age;   
  27.          }   
  28.   
  29.          // 设置年龄值   
  30.          public void setAge(int age) {   
  31.                    this .age = age;   
  32.          }   
  33.   
  34.          // 执行注册方法   
  35.          public String execute() throws Exception {   
  36.                    FORWARD = "success";   
  37.                    return FORWARD ;   
  38.          }   
  39.     
  40.          // 校验方法,用来输入校验   
  41.          public void validate() {   
  42.                    // 校验是否输入用户名   
  43.                    if (getUsername() == null || getUsername().trim().equals("")) {                      
  44.                             addFieldError("username"" 请输入用户名 ");   
  45.                    }   
  46.                    // 校验是否输入生日   
  47.                    if (getBirthday()==null ){   
  48.                             addFieldError("birthday"" 请输入生日日期 ");   
  49.                    }else  
  50.                    // 校验是否输入正确的生日日期   
  51.                    if (getBirthday().after(new Date())){   
  52.                             addFieldError("birthday"" 请不要输入未来日期 ");   
  53.                    }   
  54.                    // 校验是否输入密码   
  55.                    if (getPassword() == null || getPassword().trim().equals("")) {                        
  56.                             addFieldError("password"" 请输入密码 ");   
  57.                    }   
  58.                    // 校验是否输入确认密码   
  59.                    if (getRepassword() == null || getRepassword().trim().equals("")) {                       
  60.                             addFieldError("repassword"" 请输入确认密码 ");   
  61.                    }   
  62.                    // 校验输入的密码和确认密码是否一致   
  63.                    if (!getPassword().equals(getRepassword())) {                              
  64.                             addFieldError("repassword"" 确认密码和密码输入不一致 ");   
  65.                    }   
  66.                    // 校验输入的手机号码长度是否正确   
  67.                    if (getMobile().length()!=11) {                         
  68.                             addFieldError("mobile"" 请输入正确的手机号码 ");   
  69.                    }   
  70.                    // 校验输入的年龄是否正确   
  71.                    if (getAge()<1||getAge()>99) {                        
  72.                             addFieldError("age"" 请输入您的真实年龄 ");   
  73.                    }                   
  74.          }   
  75. }   
<!------------ 文件名: RegisterAction.java---------------->
public class RegisterAction extends ActionSupport {
         // Action 类公用私有变量,用来做页面导航标志
         private static String FORWARD = null ;

         // 用户名属性
         private String username;
………
         // 年龄属性
         private int age;

         // 取得用户名值
         public String getUsername() {
                   return username;
         }
 
         // 设置用户名值
         public void setUsername(String username) {
                   this .username = username;
         }

…………………

         // 取得年龄值
         public int getAge() {
                   return age;
         }

         // 设置年龄值
         public void setAge(int age) {
                   this .age = age;
         }

         // 执行注册方法
         public String execute() throws Exception {
                   FORWARD = "success";
                   return FORWARD ;
         }
 
         // 校验方法,用来输入校验
         public void validate() {
                   // 校验是否输入用户名
                   if (getUsername() == null || getUsername().trim().equals("")) {                   
                            addFieldError("username", " 请输入用户名 ");
                   }
                   // 校验是否输入生日
                   if (getBirthday()==null ){
                            addFieldError("birthday", " 请输入生日日期 ");
                   }else
                   // 校验是否输入正确的生日日期
                   if (getBirthday().after(new Date())){
                            addFieldError("birthday", " 请不要输入未来日期 ");
                   }
                   // 校验是否输入密码
                   if (getPassword() == null || getPassword().trim().equals("")) {                     
                            addFieldError("password", " 请输入密码 ");
                   }
                   // 校验是否输入确认密码
                   if (getRepassword() == null || getRepassword().trim().equals("")) {                    
                            addFieldError("repassword", " 请输入确认密码 ");
                   }
                   // 校验输入的密码和确认密码是否一致
                   if (!getPassword().equals(getRepassword())) {                           
                            addFieldError("repassword", " 确认密码和密码输入不一致 ");
                   }
                   // 校验输入的手机号码长度是否正确
                   if (getMobile().length()!=11) {                      
                            addFieldError("mobile", " 请输入正确的手机号码 ");
                   }
                   // 校验输入的年龄是否正确
                   if (getAge()<1||getAge()>99) {                     
                            addFieldError("age", " 请输入您的真实年龄 ");
                   }                
         }
} 

配置文件中的导航定义:

Java代码 Struts2讲义34(转载)
  1. <!-------------- 文件名: struts.xml---------------->    
  2. <struts>    
  3.          <!-- Action 所在包定义 -->    
  4.          <package name="C08.1.1" extends="struts-default">    
  5.          <!-- Action 名字,类以及导航页面定义 -->    
  6.                    <!-- 通过 Action 类处理才导航的的 Action 定义 -->    
  7.                    <action name="Register"    
  8.                             class="action.RegisterAction">    
  9.                             <result name="input">/jsp/register.jsp</result>    
  10.                             <result name="success">/jsp/success.jsp</result>    
  11.                    </action>    
  12.                    <!-- 直接导航的的 Action 定义 -->    
  13.                    <action name="index" >    
  14.                             <result >/jsp/register.jsp</result>                           
  15.                    </action>    
  16.          </package>    
  17. </struts>   
<!-------------- 文件名: struts.xml----------------> 
<struts> 
         <!-- Action 所在包定义 --> 
         <package name="C08.1.1" extends="struts-default"> 
         <!-- Action 名字,类以及导航页面定义 --> 
                   <!-- 通过 Action 类处理才导航的的 Action 定义 --> 
                   <action name="Register" 
                            class="action.RegisterAction"> 
                            <result name="input">/jsp/register.jsp</result> 
                            <result name="success">/jsp/success.jsp</result> 
                   </action> 
                   <!-- 直接导航的的 Action 定义 --> 
                   <action name="index" > 
                            <result >/jsp/register.jsp</result>                        
                   </action> 
         </package> 
</struts> 

 

输入校验的数据输入 JSP 文件:

Java代码 Struts2讲义34(转载)
  1. <!------------------- 文件名: register.jsp--------------------->    
  2. …………    
  3.                   <!-- 用户信息注册 form 表单 -->    
  4.          <s:form action="Register">    
  5.                    <table width="60%" height="76" border="0">    
  6.                                      <!-- 各标签定义 -->    
  7.                                      <s:textfield name="username" label=" 用户名 "/>    
  8.                                      <s:password name="password" label=" 密   码 " />    
  9.                                      <s:password name="repassword" label=" 密   码确认 " />    
  10.                                      <s:textfield name="birthday" label=" 生日 "/>    
  11.                                      <s:textfield name="mobile" label=" 手机号码 "/>    
  12.                                      <s:textfield name="age" label=" 年龄 "/>    
  13.                                      <s:submit value=" 注册 " align="center"/>                                  
  14.                    </table>    
  15.          </s:form>   
<!------------------- 文件名: register.jsp---------------------> 
………… 
                  <!-- 用户信息注册 form 表单 --> 
         <s:form action="Register"> 
                   <table width="60%" height="76" border="0"> 
                                     <!-- 各标签定义 --> 
                                     <s:textfield name="username" label=" 用户名 "/> 
                                     <s:password name="password" label=" 密   码 " /> 
                                     <s:password name="repassword" label=" 密   码确认 " /> 
                                     <s:textfield name="birthday" label=" 生日 "/> 
                                     <s:textfield name="mobile" label=" 手机号码 "/> 
                                     <s:textfield name="age" label=" 年龄 "/> 
                                     <s:submit value=" 注册 " align="center"/>                               
                   </table> 
         </s:form> 

 

数据不进行任何输入显示出错信息如图 8.1

Struts2讲义34(转载)

8.1  输入校验发现数据没有进行任何输入

输入密码不一致时的出错信息显示如图 8.2

Struts2讲义34(转载)

8.2  密码输入不一致时出错信息显示

代码解释

1 Struts2 对输入校验这方面采用的最基本方法是在每个 Action 里继承 ActionSupport 类,并且重写它的输入校验方法 validate() 。本示例中的 RegisterAction 代码中也显示,根据页面上输入的各种校验将所有不符合输入校验规则的错误信息都由 ActionSupport 类中另一个方法 addFieldError 方法将错误信息加入到表单错误信息,并且在输入数据的页面显示,而不会再由 Action 导航到注册成功页面。 struts.xml 也定义了 1 个名字为“ input ”的 result ,它表明将所有输入失败的错误信息导航到一个特定页面。本示例中笔者还是将这个特定页面定义为数据输入的页面。

2 )再次阅读 RegisterAction 代码,可以发现在 validate 方法里笔者编写了很多 if 语句。每一个 if 语句中都针对表单中某一字段进行输入校验。如果发现不符合输入校验规则则都调用 addFieldError 方法。该方法中有两个参数,第 1 个参数都是表单中字段名,这里所有的名字都和输入数据的页面中每一个字段的 name 属性中内容相同。否则 Struts2 是找不到具体错误信息是针对哪一个字段。第 2 个参数则是错误信息的内容。这些内容就是在输入校验失败时候显示在之前所说的特定页面中的。由图 7.1 和图 7.2 可以看到这些内容在页面上是如何显示的。

3 validate 方法中的各个 if 语句判断了表单中各个字段的输入数据是否符合输入校验的规则,这些规则也是开发人员根据特定业务逻辑定义的。比如其中数据是否输入,输入的生日信息是否在当前日期之前等等。这里细心读者又可以发现并没有对这些字段进行类型转换,但在 Action 中某些字段类型都已经变成 Java 的一些基本类型。比如生日字段,页面上输入时候是字符串,在 Action 中已经变成 Java 中的 Date 类型。因为之前在类型转换章节也已说明:页面上输入的数据已经都由字符串类型转换成 Action 中指定的 Java 类型。因此从这一点更加说明类型转换是输入校验的基础,也可以说是 1 种特定的输入校验。

8.1.2   validateXXX 方法进行输入校验

技术要点

本节代码也就 1 个简单的用户注册功能具体介绍利用 validateXXX 方法对 Action 中某一特定的方法进行校验。

Action 具体方法的 validateXXX 方法介绍。

演示代码

使用的 Action 文件:

Java代码 Struts2讲义34(转载)
  1. <!----------- 文件名: RegisterAction.java------------------>    
  2. public class RegisterAction extends ActionSupport {    
  3.          // Action 类公用私有变量,用来做页面导航标志    
  4.          private static String FORWARD = null ;    
  5.          // 用户名属性    
  6.          private String username;    
  7. ………    
  8.          // 年龄属性    
  9.          private int age;    
  10.          // 取得用户名值    
  11.          public String getUsername() {    
  12.                    return username;    
  13.          }    
  14.   
  15.          // 设置用户名值    
  16.          public void setUsername(String username) {    
  17.                    this .username = username;    
  18.          }    
  19. …………………    
  20.          // 取得年龄值    
  21.          public int getAge() {    
  22.                    return age;    
  23.          }    
  24.     
  25.          // 设置年龄值    
  26.          public void setAge(int age) {    
  27.                    this .age = age;    
  28.          }    
  29.     
  30.          // 执行注册方法    
  31.          public String Register() throws Exception {    
  32.                    FORWARD = "success";    
  33.                    return FORWARD ;    
  34.          }    
  35.      
  36.          // 校验方法,用来输入校验    
  37.          public void validateRegister() {    
  38.                    // 校验是否输入用户名    
  39.                   if (getUsername() == null || getUsername().trim().equals("")) {                       
  40.                             addFieldError("username"" 请输入用户名 ");    
  41.                    }    
  42.                    // 校验是否输入生日    
  43.                    if (getBirthday()==null ){    
  44.                             addFieldError("birthday"" 请输入生日日期 ");    
  45.                    }else    
  46.                    // 校验是否输入正确的生日日期    
  47.                    if (getBirthday().after(new Date())){    
  48.                             addFieldError("birthday"" 请不要输入未来日期 ");    
  49.                    }    
  50.                    // 校验是否输入密码    
  51.                    if (getPassword() == null || getPassword().trim().equals("")) {                         
  52.                             addFieldError("password"" 请输入密码 ");    
  53.                    }    
  54.                    // 校验是否输入确认密码    
  55.                   if (getRepassword() == null || getRepassword().trim().equals("")) {                        
  56.                             addFieldError("repassword"" 请输入确认密码 ");    
  57.                    }    
  58.                    // 校验输入的密码和确认密码是否一致    
  59.                    if (!getPassword().equals(getRepassword())) {                               
  60.                             addFieldError("repassword"" 确认密码和密码输入不一致 ");    
  61.                    }    
  62.                    // 校验输入的手机号码长度是否正确    
  63.                    if (getMobile().length()!=11) {                          
  64.                             addFieldError("mobile"" 请输入正确的手机号码 ");    
  65.                    }    
  66.                    // 校验输入的年龄是否正确    
  67.                    if (getAge()<1||getAge()>99) {                         
  68.                             addFieldError("age"" 请输入您的真实年龄 ");    
  69.                    }                    
  70.     }    
  71. }   
<!----------- 文件名: RegisterAction.java------------------> 
public class RegisterAction extends ActionSupport { 
         // Action 类公用私有变量,用来做页面导航标志 
         private static String FORWARD = null ; 
         // 用户名属性 
         private String username; 
……… 
         // 年龄属性 
         private int age; 
         // 取得用户名值 
         public String getUsername() { 
                   return username; 
         } 

         // 设置用户名值 
         public void setUsername(String username) { 
                   this .username = username; 
         } 
………………… 
         // 取得年龄值 
         public int getAge() { 
                   return age; 
         } 
 
         // 设置年龄值 
         public void setAge(int age) { 
                   this .age = age; 
         } 
 
         // 执行注册方法 
         public String Register() throws Exception { 
                   FORWARD = "success"; 
                   return FORWARD ; 
         } 
  
         // 校验方法,用来输入校验 
         public void validateRegister() { 
                   // 校验是否输入用户名 
                  if (getUsername() == null || getUsername().trim().equals("")) {                    
                            addFieldError("username", " 请输入用户名 "); 
                   } 
                   // 校验是否输入生日 
                   if (getBirthday()==null ){ 
                            addFieldError("birthday", " 请输入生日日期 "); 
                   }else 
                   // 校验是否输入正确的生日日期 
                   if (getBirthday().after(new Date())){ 
                            addFieldError("birthday", " 请不要输入未来日期 "); 
                   } 
                   // 校验是否输入密码 
                   if (getPassword() == null || getPassword().trim().equals("")) {                      
                            addFieldError("password", " 请输入密码 "); 
                   } 
                   // 校验是否输入确认密码 
                  if (getRepassword() == null || getRepassword().trim().equals("")) {                     
                            addFieldError("repassword", " 请输入确认密码 "); 
                   } 
                   // 校验输入的密码和确认密码是否一致 
                   if (!getPassword().equals(getRepassword())) {                            
                            addFieldError("repassword", " 确认密码和密码输入不一致 "); 
                   } 
                   // 校验输入的手机号码长度是否正确 
                   if (getMobile().length()!=11) {                       
                            addFieldError("mobile", " 请输入正确的手机号码 "); 
                   } 
                   // 校验输入的年龄是否正确 
                   if (getAge()<1||getAge()>99) {                      
                            addFieldError("age", " 请输入您的真实年龄 "); 
                   }                 
    } 
} 

 

配置文件中的导航定义同 8.1.1 。输入校验的数据输入 JSP 文件同 8.1.1 有一点不同,具体代码如下:

Java代码 Struts2讲义34(转载)
  1. <!------------ 文件名: register.jsp---------------->    
  2. …………    
  3.     <!-- fielderror 标签显示所有校验错误信息 -->    
  4.       <s:fielderror></s:fielderror>    
  5.          <!-- 用户信息注册 form 表单 -->    
  6.          <s:form action="Register!Register.action ">    
  7.                    <table width="60%" height="76" border="0">    
  8.                                      <!-- 各标签定义 -->    
  9.                                      <s:textfield name="username" label=" 用户名 "/>    
  10.                                      <s:password name="password" label=" 密   码 " />    
  11.                                      <s:password name="repassword" label=" 密   码确认 " />    
  12.                                      <s:textfield name="birthday" label=" 生日 "/>    
  13.                                      <s:textfield name="mobile" label=" 手机号码 "/>    
  14.                                      <s:textfield name="age" label=" 年龄 "/>    
  15.                                      <s:submit value=" 注册 " align="center"/>                                  
  16.                    </table>    
  17.          </s:form>   
<!------------ 文件名: register.jsp----------------> 
………… 
    <!-- fielderror 标签显示所有校验错误信息 --> 
      <s:fielderror></s:fielderror> 
         <!-- 用户信息注册 form 表单 --> 
         <s:form action="Register!Register.action "> 
                   <table width="60%" height="76" border="0"> 
                                     <!-- 各标签定义 --> 
                                     <s:textfield name="username" label=" 用户名 "/> 
                                     <s:password name="password" label=" 密   码 " /> 
                                     <s:password name="repassword" label=" 密   码确认 " /> 
                                     <s:textfield name="birthday" label=" 生日 "/> 
                                     <s:textfield name="mobile" label=" 手机号码 "/> 
                                     <s:textfield name="age" label=" 年龄 "/> 
                                     <s:submit value=" 注册 " align="center"/>                               
                   </table> 
         </s:form> 

 

数据不进行任何输入显示出错信息如图 8.3

Struts2讲义34(转载)

8.3  输入校验发现数据没有进行任何输入

输入密码不一致时的出错信息显示如图 8.4

Struts2讲义34(转载)

8.4  密码输入不一致时出错信息显示

代码解释

1 Struts2 中除了 validate 方法之外它还有 1 validateXXX 方法针对 Action 中某一特定方法进行该方法的各种字段的输入校验。其中 XXX 就是该特定方法名。在本示例中笔者定义了一个 Register 方法,该方法和上一小节中的 execute 方法类似只是一个简单的导航。但是在该 RegisterAction 中就没有了 validate 方法,取而代之的是 validateRegister 方法。

注意:如果读者使用 validateRegister 方法,那最好不要再使用 validate 方法。虽然和上一小节示例代码比较这两个方法里的内容是完全一摸一样的,但是 validate 方法是对所有 Action 中方法的输入校验都进行校验, validateRegister 方法只对 Register 方法进行校验。因此两者不能重复使用,都使用会造成两个方法都进行了校验的结果。执行顺序是先 validateRegister validate 。如果 validateRegister 方法有特殊的输入校验则就会被 validate 方法“覆盖”,因此达不到预期的输入校验目的。推荐读者自己进行试验,在这两个方法里设置断点运行一下就知道了。

2 )请读者阅读数据输入的页面代码,在代码中笔者用黑体标注的是 1 个特殊的运行 Action 的示例。这里笔者故意把名字都写为“ Register ”来让读者加深理解。首先第 1 个“ Register ”是 RegisterAction 中的方法名,一定要和方法名写成一样。而在“!”后的“ Register ”则是在 struts.xml 配置文件中定义的 RegisterAction 的映射里的“ name ”内容。黑体的内容表明该表单的 Action 是执行 RegisterAction 中的 Register 方法。如果在页面中直接写“!”后面的内容则表示执行的是 RegisterAction 中的 execute 方法。在图 8.3 浏览器中的 URL 笔者也有红框圈中,表明该表单数据输入完成后提交时候执行的是 Register 方法。这是 Struts2 中一个特殊的使用方式。如果开发者以后在使用 Struts2 的开发工作中根据特定业务逻辑不想执行 execute 方法而是执行另外一个开发完成的方法。则在视图页面(包括 velocity freemarker )中可以以这种方式让表单提交后执行该开发完成的方法。

3 validateRegister 方法中各个if语句定义和上一小节的validate方法内容相同。这里笔者只是作为示例所以两个方法中的内容相同,其实可以和上一小节中的validate方法的内容不相同,用来进行Register方法中特定的表单字段输入校验。

4)在数据输入的视图界面笔者又增加了Struts2的标签fielderror。图8.3和图8.4也可以看出在具体字段输入校验出错信息显示之外。在表单头部也有出错信息显示。这其实和Struts2的校验顺序有关。

在之前说明validateRegister方法和validate方法时候也记述了两者的执行校验顺序是先validateRegistervalidate。其实在视图界面进行表单提交后。输入校验顺序是以如下的顺序:

l         查找Action中是否有validateXXX方法。如果有则执行该方法。将校验产生的错误信息放置到ActionContext对象中。

l         查找Action中是否有validate方法。如果有则执行该方法。将校验产生的错误信息放置到ActionContext对象中。

l         查找视图界面是否有fielderror标签定义。如果有则返回到result为“input”的视图。同时ActionContext对象中有关的输入校验的错误信息也显示在该视图中。

Struts2的输入校验顺序就是按照如上说明来先后执行的,这也更好的说明了validateRegister方法和validate方法并存在Action时候输入校验是如何进行的。这也是图8.3和图8.4会产生错误信息显示两遍现象的原因。