零基础学Struts
上QQ阅读APP看书,第一时间看更新

5.4 使用校验框架完成输入校验

前面介绍了Struts 2中内置的校验器,下面将通过这些校验器来改写注册案例。同时将结合国际化处理来实现国际化信息提示,并通过配置来添加客户端校验。

5.4.1 完成输入校验

前面介绍了Struts 2中内置的校验器。现在开始使用这些校验器来改写以前那个注册案例。不再使用手动添加校验代码的方式,而是使用编写校验规则文件的方式来进行输入校验。

下面来看有哪些输入校验规则。

(1)用户名、密码、确认密码必须输入。

(2)用户名只能是数字或者字母,长度为6~20之间。

(3)密码、确认密码必须是数字或者字母,长度为6~20之间。

(4)密码和确认密码必须相同。

(5)年龄必须为整数而且必须是有效的年龄值。

(6)出生日期必须为正确的日期格式,如1988-01-03,而且只能是1900-1-1~2010-1-1之间。

(7)邮箱地址必须为合法的邮箱地址。

首先新建一个用户注册页。在该注册页中使用Sturts 2的表单标签来设计表单,代码如下所示。

        <%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>
        <%@ taglib prefix="s" uri="/struts-tags" %>
        <! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
        <html>
          <head>
            <title>注册页面</title>
          </head>
          <body>
            <! -- 注册表单 -->
            <center>
            <h2>注册页面</h2>
            <s:form action="register">
                  <s:textfield name="username" label="用户名"></s:textfield>
                  <s:password name="password" label="密码"></s:password>
                  <s:password name="repassword" label="确认密码"></s:password>
                  <s:textfield name="age" label="年龄"></s:textfield>
                  <s:textfield name="birth" label="出生日期"></s:textfield>
                  <s:textfield name="email" label="邮箱地址"></s:textfield>
                  <s:submit value="注册"></s:submit>
            </s:form>
            </center>
          </body>
        </html>

新建业务控制器Action,类名为RegisterAction。该Action继承ActionSupport类,并添加相应的属性以及属性的setter和getter方法。与前面不同的是,这里不用提供validate()方法,代码如下所示。

        package net.hncu.action;
        import java.util.Calendar;
        import java.util.Date;
        import java.util.regex.Pattern;
    import com.opensymphony.xwork2.ActionSupport;
    public class RegisterAction extends ActionSupport{
          private String username;
          private String password;
          private String repassword;
          private int age;
          private Date birth;
          private String email;
          public String getUsername() {
                return username;
          }
          public void setUsername(String username) {
                this.username = username;
          }
          public String getPassword() {
                return password;
          }
          public void setPassword(String password) {
                this.password = password;
          }
          public String getRepassword() {
                return repassword;
          }
          public void setRepassword(String repassword) {
                this.repassword = repassword;
          }
          public int getAge() {
                return age;
          }
          public void setAge(int age) {
                this.age = age;
          }
          public Date getBirth() {
                return birth;
          }
          public void setBirth(Date birth) {
                this.birth = birth;
          }
          public String getEmail() {
                return email;
          }
          public void setEmail(String email) {
                this.email = email;
          }
          public String execute() throws Exception {
                return SUCCESS;
          }
    }

为RegisterAction添加校验规则文件,文件名为“RegisterAction-validation.xml”。前面介绍了该校验规则文件可以采用两种配置风格。首先采用字段校验器配置风格编写该校验文件,代码如下所示。

        <? xml version="1.0" encoding="UTF-8"? >
        <! DOCTYPE validators PUBLIC
                "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
                "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
        <validators>
              <field name="username">
                      <field-validator type="requiredstring">
                            <param name="trim">true</param>
                            <message>必须输入用户名</message>
                      </field-validator>
                      <field-validator type="regex">
                            <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                            <message>用户名长度必须为620之间</message>
                      </field-validator>
              </field>
              <field name="password">
                      <field-validator type="requiredstring">
                            <param name="trim">true</param>
                            <message>必须输入密码</message>
                      </field-validator>
                      <field-validator type="regex">
                            <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                            <message>密码长度必须为620之间</message>
                      </field-validator>
              </field>
              <field name="repassword">
                      <field-validator type="requiredstring">
                            <param name="trim">true</param>
                            <message>必须输入确认密码</message>
                      </field-validator>
                      <field-validator type="regex">
                            <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                            <message>确认密码长度必须为620之间</message>
                      </field-validator>
                      <field-validator type="fieldexpression">
                            <param name="expression"><! [CDATA[(repassword==password)]]></param>
                            <message>密码和确认密码必须一致</message>
                      </field-validator>
              </field>
              <field name="age">
                      <field-validator type="int">
                            <param name="min">1</param>
                            <param name="max">120</param>
                            <message>年龄必须在${min}${max}之间</message>
                      </field-validator>
              </field>
          <field name="birth">
                  <field-validator type="date">
                        <param name="min">1900-01-01</param>
                        <param name="max">2010-01-01</param>
                        <message>出生日期必须在1900-01-012010-01-01之间</message>
                  </field-validator>
          </field>
          <field name="email">
                  <field-validator type="email">
                        <message>请输入有效的电子邮箱地址</message>
                  </field-validator>
          </field>
    </validators>

同样也可以使用非字段校验器配置风格编写该校验规则文件,代码如下所示。

    <? xml version="1.0" encoding="UTF-8"? >
    <! DOCTYPE validators PUBLIC
            "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
            "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
    <validators>
          <validator type="requiredstring">
                <param name="fieldName">username</param>
                <param name="trim">true</param>
                <message>必须输入用户名</message>
          </validator>
          <validator type="regex">
                <param name="fieldName">username</param>
                <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                <message>用户名长度必须为620之间</message>
          </validator>
          <validator type="requiredstring">
                <param name="fieldName">password</param>
                <param name="trim">true</param>
                <message>必须输入密码</message>
          </validator>
          <validator type="regex">
                <param name="fieldName">password</param>
                <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                <message>密码长度必须为620之间</message>
          </validator>
          <validator type="requiredstring">
                <param name="fieldName">repassword</param>
                <param name="trim">true</param>
                <message>必须输入确认密码</message>
          </validator>
          <validator type="regex">
                <param name="fieldName">repassword</param>
                <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                <message>确认密码长度必须为620之间</message>
              </validator>
              <validator type="fieldexpression">
                    <param name="fieldName">repassword</param>
                    <param name="expression"><! [CDATA[(repassword==password)]]></param>
                    <message>密码和确认密码必须一致</message>
              </validator>
              <validator type="int">
                    <param name="fieldName">age</param>
                    <param name="min">1</param>
                    <param name="max">120</param>
                    <message>年龄必须在${min}${max}之间</message>
              </validator>
              <validator type="date">
                    <param name="fieldName">birth</param>
                    <param name="min">1900-01-01</param>
                    <param name="max">2010-01-01</param>
                    <message>出生日期必须在1900-01-012010-01-01之间</message>
              </validator>
              <validator type="email">
                    <param name="fieldName">email</param>
                    <message>请输入有效的电子邮箱地址</message>
              </validator>
        </validators>

不要忘了在“struts.xml”文件中配置RegisterAction,这里不再重复介绍。现在打开用户注册页,不输入任何数据,单击“注册”按钮进行注册。因为输入的用户信息不满足校验规则,从而使得输入校验失败,那么页面将提示要求进行输入的错误信息,如图5.16所示。

同样当输入一些不满足校验规则的用户信息,都会使得输入校验失败,那么页面将跳转回注册页,并提示要求输入合法信息的错误信息,如图5.17所示。

图5.16 注册页面(1)

图5.17 注册页面(2)

5.4.2 增加客户端校验

Struts 2还提供了一个非常好的功能,就是能通过校验规则文件自动添加客户端输入校验。先来看下如何来添加客户端校验。非常简单,只要在<s:form>标签中添加validate=“true”属性即可,代码如下所示。

        <s:form action="register" validate="true">
              <s:textfield name="username" label="用户名"></s:textfield>
              <s:password name="password" label="密码"></s:password>
              <s:password name="repassword" label="确认密码"></s:password>
              <s:textfield name="age" label="年龄"></s:textfield>
              <s:textfield name="birth" label="出生日期"></s:textfield>
              <s:textfield name="email" label="邮箱地址"></s:textfield>
              <s:submit value="注册"></s:submit>
        </s:form>

刷新一下用户注册页,并查看该注册页的源代码。从页面源代码中可以看出,Struts 2自动为其注册页面添加了用于客户端校验的JavaScript代码,如下所示。

        <script type="text/javascript">
            function validateForm_register() {
                  form = document.getElementById("register");
                  clearErrorMessages(form);
                  clearErrorLabels(form);
                  var errors = false;
        ...
        ...
                  // field name: email
                  // validator name: email
                  if (form.elements[email' ]) {
                      field = form.elements[email' ];
                      var error = "请输入有效的电子邮箱地址";
                      if (field.value ! = null && field.value.length > 0 &&
        field.value.match(/\b(^[_A-Za-z0-9-]+(\.[_A-Za-z0-9-]+)*@([A-Za-z0-9-])+(\.[A-Za-
        z0-9-]+)*((\.[A-Za-z0-9]{2, })—(\.[A-Za-z0-9]{2, }\.[A-Za-z0-9]{2, }))$)\b/gi)==null) {
                          addError(field, error);
                          errors = true;
                      }
                  }
                  return ! errors;
            }
        </script>

这时如果输入不符合校验规则的用户信息。页面将提示要求进行输入的错误信息,如图5.18所示。

这里与前面不同的是,这些错误信息是客户端校验产生的。注意观察一下地址栏就会发现,页面仍然是用户注册页,表示并未提交到对应的Action。也就是说校验过程是使用客户端校验来完成的。

那是不是以后都可以不用自己编写客户端校验代码,直接使用Struts 2提供的这个客户端校验功能就行了?其实并不是这样的,起码现在来讲不是这样的。因为Struts 2提供的这个客户端校验功能不是很完美,在很多地方都有欠缺。比如不能检查密码和确认密码是否相同,不能检查日期是否合法等。

图5.18 注册页面(1)

图5.19 注册页面(2)

客户端校验仅仅支持如下校验器。

❑ required(必填校验器)

❑ requiredstring(必填字符串校验器)

❑ int(整数校验器)

❑ double(双精度浮点数校验器)

❑ email(邮箱地址校验器)

❑ url(网址校验器)

❑ regex(正则表达式校验器)

因为客户端校验支持的校验器不多,在开发中无法满足客户端校验规则,所以还是希望能通过自己编写JavaScript代码来实现客户端输入校验。

5.4.3 国际化提示信息

下面来为程序添加国际化功能。首先来编写国际化资源文件,并将错误提示以键值对的形式保存在资源文件中,代码如下所示。

        age.rule =年龄必须在${min}${max}之间
        birth.rule = 出生日期必须在1900-01-012010-01-01之间
        email.rule = 请输入有效的电子邮箱地址
        password.required = 必须输入密码
        password.rule       = 密码长度必须为620之间
        repassword.equals    = 密码和确认密码必须一致
        repassword.required = 必须输入确认密码
        repassword.rule       = 确认密码长度必须为620之间
        username.required = 必须输入用户名
        username.rule       = 用户名长度必须为620之间

别忘了在“struts.xml”文件中使用全局属性加载该国际化资源文件,代码如下所示。

        <constant name="struts.custom.i18n.resources" value="myMessage"></constant>

国际化资源文件创建好了,如何才能让校验规则文件找到对应的错误提示信息呢?只要找到校验规则文件中的message元素,并在该元素中通过key属性指定国际化资源文件中对应的key即可,代码如下所示。

        <? xml version="1.0" encoding="UTF-8"? >
        <! DOCTYPE validators PUBLIC
                "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
                "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
        <validators>
              <field name="username">
                    <field-validator type="requiredstring">
                            <param name="trim">true</param>
                            <message key="username.required"></message>
                    </field-validator>
                    <field-validator type="regex">
                            <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                            <message key="username.rule"></message>
                    </field-validator>
              </field>
              <field name="password">
                    <field-validator type="requiredstring">
                            <param name="trim">true</param>
                            <message key="password.required"></message>
                    </field-validator>
                    <field-validator type="regex">
                            <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                            <message key="password.rule"></message>
                    </field-validator>
              </field>
              <field name="repassword">
                    <field-validator type="requiredstring">
                            <param name="trim">true</param>
                            <message key="repassword.required"></message>
                    </field-validator>
                    <field-validator type="regex">
                            <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                            <message key="repassword.rule"></message>
                    </field-validator>
                    <field-validator type="fieldexpression">
                            <param name="expression"><! [CDATA[(repassword==password)]]></param>
                            <message key="repassword.equals"></message>
                    </field-validator>
              </field>
              <field name="age">
                    <field-validator type="int">
                            <param name="min">1</param>
                            <param name="max">120</param>
                            <message key="age.rule"></message>
                    </field-validator>
              </field>
              <field name="birth">
                      <field-validator type="date">
                            <param name="min">1900-01-01</param>
                            <param name="max">2010-01-01</param>
                            <message key="birth.rule"></message>
                      </field-validator>
              </field>
              <field name="email">
                      <field-validator type="email">
                            <message key="email.rule"></message>
                      </field-validator>
              </field>
        </validators>

这里暂时先去掉客户端校验功能。删除注册页面中fom元素的validate=true属性。现在再打开用户注册页,不输入任何数据,单击“注册”按钮。因为输入的用户信息不满足校验规则,从而使得输入校验失败,那么页面将提示要求进行输入的错误信息,如图5.20所示。同样可以添加英文的资源文件,使得页面能提示英文的错误信息。

5.4.4 客户端校验与国际化问题

之所以开始要去掉客户端校验功能。是因为同时使用客户端校验功能和国际化功能会出现异常。现在为注册页面的form元素添加validate=true属性,增加客户端校验功能。这时再打开用户输入页,发现页面出现了异常,如图5.21所示。

图5.20 注册页面

图5.21 页面异常

这时大家要有耐心,发现问题要想办法解决而不是垂头丧气的。先来看看为什么会出现这样的问题。添加的国际化信息肯定是没有错误的,在使用服务器端校验时已经测试过了。那么是不是使用客户端校验无法找到那些国际化信息呢?好,现在有思路了,通过另一种方式来读取国际化信息。在校验规则文件中使用${getTest(“key”)}来读取国际化文件中的信息,代码如下所示。

    <? xml version="1.0" encoding="UTF-8"? >
    <! DOCTYPE validators PUBLIC
            "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
            "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
    <validators>
          <field name="username">
                  <field-validator type="requiredstring">
                        <param name="trim">true</param>
                        <message>${getText("username.required")}</message>
                  </field-validator>
                  <field-validator type="regex">
                        <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                        <message>${getText("username.rule")}</message>
                  </field-validator>
          </field>
          <field name="password">
                  <field-validator type="requiredstring">
                        <param name="trim">true</param>
                        <message>${getText("password.required")}</message>
                  </field-validator>
                  <field-validator type="regex">
                        <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                        <message>${getText("password.rule")}</message>
                  </field-validator>
          </field>
          <field name="repassword">
                  <field-validator type="requiredstring">
                        <param name="trim">true</param>
                        <message>${getText("repassword.required")}</message>
                  </field-validator>
                  <field-validator type="regex">
                        <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                        <message>${getText("repassword.rule")}</message>
                  </field-validator>
                  <field-validator type="fieldexpression">
                        <param name="expression"><! [CDATA[(repassword==password)]]></param>
                        <message>${getText("repassword.equals")}</message>
                  </field-validator>
          </field>
          <field name="age">
                  <field-validator type="int">
                        <param name="min">1</param>
                        <param name="max">120</param>
                        <message>${getText("age.rule")}</message>
                  </field-validator>
          </field>
          <field name="birth">
                  <field-validator type="date">
                        <param name="min">1900-01-01</param>
                                <param name="max">2010-01-01</param>
                                <message>${getText("birth.rule")}</message>
                          </field-validator>
                  </field>
                  <field name="email">
                          <field-validator type="email">
                                <message>${getText("email.rule")}</message>
                          </field-validator>
                  </field>
            </validators>

上面代码中使用的是在ActionSupport类中的getText方法来确定的国际化文件中的信息。这时再次运行用户注册页,发现不再有异常。同时当输入不符合校验规则的用户信息时,页面将提示相应的错误信息。同样,通过观察浏览器地址栏可以发现这些错误信息是客户端校验产生的,如图5.22所示。

图5.22 注册页面

5.4.5 校验短路

有一点不能忽略掉,就是当不输入任何信息,直接单击“注册”按钮时进行注册时,发现“用户名”文本框上显示了两条校验错误提示信息。这种提示信息是非常不友好的,用户还没有输入用户名,就提示用户名长度必须为6~20之间。

显然这样的错误信息太不友好了,那么有什么方式来解决呢?首先来想想看,为什么会出现两条错误信息。因为是针对一个字段两个校验器都校验失败了,这样才会有两条错误信息。所以就可以采用一种短路的方式来解决这个问题。短路,也就是说一个校验器校验失败了另一个校验器不会再进行校验。

要想实现短路这种效果,只需在field-validtor元素(字段校验器配置风格)或者是valitor元素(非字段校验器风格)中增加一个short-circuit属性,并设置其属性值为true就行了,代码如下所示。

        <? xml version="1.0" encoding="UTF-8"? >
        <! DOCTYPE validators PUBLIC
                "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
                "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
        <validators>
              <field name="username">
                      <field-validator type="requiredstring" short-circuit="true">
                            <param name="trim">true</param>
                            <message>${getText("username.required")}</message>
                      </field-validator>
                      <field-validator type="regex">
                            <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                            <message>${getText("username.rule")}</message>
                      </field-validator>
              </field>
              <field name="password">
                      <field-validator type="requiredstring" short-circuit="true">
                            <param name="trim">true</param>
                            <message>${getText("password.required")}</message>
                      </field-validator>
                      <field-validator type="regex">
                            <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                            <message>${getText("password.rule")}</message>
                      </field-validator>
              </field>
              <field name="repassword">
                      <field-validator type="requiredstring" short-circuit="true">
                            <param name="trim">true</param>
                            <message>${getText("repassword.required")}</message>
                      </field-validator>
                      <field-validator type="regex" short-circuit="true">
                            <param name="expression"><! [CDATA[(\w{6,20})]]></param>
                            <message>${getText("repassword.rule")}</message>
                      </field-validator
                      <field-validator type="fieldexpression">
                            <param name="expression"><! [CDATA[(repassword==password)]]></param>
                            <message>${getText("repassword.equals")}</message>
                      </field-validator>
              </field>
              <field name="age">
                      <field-validator type="int">
                            <param name="min">1</param>
                            <param name="max">120</param>
                            <message>${getText("age.rule")}</message>
                      </field-validator>
              </field>
              <field name="birth">
                      <field-validator type="date">
                            <param name="min">1900-01-01</param>
                            <param name="max">2010-01-01</param>
                            <message>${getText("birth.rule")}</message>
                      </field-validator>
              </field>
              <field name="email">
                      <field-validator type="email">
                            <message>${getText("email.rule")}</message>
                      </field-validator>
              </field>
        </validators>

这时再打开用户注册页,不输入任何数据,单击“注册”按钮进行注册。现在错误提示不再有两条,而只有一条错误信息,如图5.23所示。

图5.23 注册页面

注意

客户端校验不支持校验器短路,校验器短路暂时只能在服务器端校验中使用。

5.4.6 校验规则文件搜索规则

在前面曾提到过在Action中可以包含多个处理逻辑,不同的处理逻辑可能需要不同的校验规则,假设现在有如下“struts.xml”文件。

        <struts>
              <package name="struts2" extends="struts-default">
                  <! -- 定义registerAction,其实现类为net.hncu.action.RegisterAction-->
                  <action name="register" class="net.hncu.action.RegisterAction">
                      <! -- 定义处理结果与视图资源之间的关系-->
                      <result name="success">/result.jsp</result>
                      <result name="input">/register.jsp</result>
                  </action>
                  <! -- 定义deleteUserAction,其实现类为net.hncu.action.RegisterAction,
                  采用deleteUser方法来处理-->
                  <action name="deleteUser" class="net.hncu.action.RegisterAction" method="deleteUser">
                      <result name="success">/result.jsp</result>
                      <result name="input">/register.jsp</result>
                  </action>
            </package>
          </struts>

这时如果要为deleteUser处理逻辑添加校验规则文件,只需要创建如下文件名格式的文件。

        <ActionName>-<ActionAliasName>-validation.xml

其中ActionName对应处理Action的类名。ActionAliasName是该处理逻辑在“struts.xml”中对应的name属性。如果需要为deleteUser处理逻辑添加校验规则,校验规则文件名为“RegisterAction-deleteUser-validation.xml”,同样将该文件存放到对应Action编译后的classe文件目录中。

这里同样要注意的是,对deleteUser处理逻辑进行校验时,会先后调用“RegisterAction-validation.xml”和“RegisterAction-deleteUser-validation.xml”这两个校验规则。