Java-Struts学习笔记之Struts框架输入校验、国际化及异常处理

项目文件结构
文件结构
文件结构

Struts2的输入校验

Struts2输入校验、执行流程:
1)首先进行类型转换
2)然后进行输入校验(执行validate方法)
3)如果在上述过程中出现了任何错误,都不会再去执行 execute方法,会转向 struts.xml 中该 action 的名为 input 的 result 所对应的页面。

要点:

  1. ActionSupport 类的 addActionError()方法的实现:首先创建一个ArrayList对象,然后将错误消息添加到该 ArrayList对象中。

  2. 当调用 getActionErrors()方法返回 Action 级别的错误信息列表时,返回的实际上是集合的一个副本而不是集合本身,因此对集合副本调用 clear()方法清除的依旧是副本中的元素而非原集合中的元素,此时原集合中的内容没有收到任何的影响。换句话说,Action级别的错误信息列表对开发者来说是只读的。

  3. Field Error 级别的错误信息底层是用 LinkedHashMap 实现的,该Map 的 key 是 String 类型,value 是 List类型,这就表示一个 Field Name 可以对应多条错误信息,这些错误信息都放置在List集合当中。

register.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
………………………………
<body>
<h2><font color="blue">用户注册</font></h2>
<s:actionerror cssStyle="color:red"/>
----------------------------------------
<s:fielderror cssStyle="color:blue"></s:fielderror>
<!--
<form action="register.action">
username: <input type="text" name="username" size="20"><br>
password: <input type="password" name="password" size="20"><br>
repassword: <input type="password" name="repassword" size="20"><br>
age: <input type="text" name="age" size="20"><br>
birthday: <input type="text" name="birthday" size="20"><br>
graduation: <input type="text" name="graduation" size="20"><br>
<input type="submit" value="submit"/>
</form>
-->
<s:form action="register.action" theme="simple"><!—-加theme="simple修饰以简单表单呈现-->
    username: <s:textfield name="username" label="username"></s:textfield><br>
    password: <s:password name="password" label="password"></s:password><br>
    repassword: <s:password name="repassword" label="repassword"></s:password><br>
    age: <s:textfield name="age" label="age"></s:textfield><br>
    birthday: <s:textfield name="birthday" label="birthday"></s:textfield><br>
    graduation: <s:textfield name="graduation" label="graduation"></s:textfield><br>
    <s:submit value="submit"></s:submit>
</s:form>
</body>
</html>

RegisterAction.java

package com.shengsiyuan.struts2;
import java.util.Calendar;
import java.util.Date;
import com.opensymphony.xwork2.ActionSupport;
public class RegisterAction extends ActionSupport
{
    private String username;
    private String password;
    private String repassword;
    private int age;
    private Date birthday;
    private Date graduation;
    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 getBirthday()
    {
        return birthday;
    }
    public void setBirthday(Date birthday)
    {
        this.birthday = birthday;
    }
    public Date getGraduation()
    {
        return graduation;
    }
    public void setGraduation(Date graduation)
    {
        this.graduation = graduation;
    }
    @Override
    public String execute() throws Exception
    {
        return SUCCESS;
    }
    @Override
    public void validate()
    {
        if(null == username || username.length() < 4 || username.length() > 6)
        {
            this.addActionError("username invalid");    //Action级别的错误
            this.addFieldError("username", "username invalid in field");//field级别的错误
        }
        if(null == password || password.length() < 4 || password.length() > 6)
        {
            this.addActionError("password invalid");
        }
        else if(null == repassword || repassword.length() < 4 || repassword.length() > 6)
        {
            this.addActionError("repassword invalid");
        }
        else if(!password.equals(repassword))
        {
            this.addActionError("the passwords not the same");
        }
        if(age < 10 || age > 50)
        {
            this.addActionError("age invalid");
        }
        if(null == birthday)
        {
            this.addActionError("birthday invalid");
        }
        if(null == graduation)
        {
            this.addActionError("graduation invalid");
        }
        if(null != birthday && null != graduation)
        {
            Calendar c1 = Calendar.getInstance();    //获取日期
            c1.setTime(birthday);
            Calendar c2 = Calendar.getInstance();
            c2.setTime(graduation);
            if(!c1.before(c2)) //如果c1不在c2前
            {
                this.addActionError("birthday should be before graduation");
            }
        }
        //this.getFieldErrors().clear();    //clear()方法清空的只是副本
        //this.getActionErrors().clear();
        //this.clearActionErrors();    //清除Action级别的错误消息
        //this.clearFieldErrors();    //清除field级别的错误消息
        System.out.println("invoked!!!");
    }
}

struts.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <package name="struts2" extends="struts-default">
        <action name="login" class="com.shengsiyuan.struts2.LoginAction" method="myExecute">    <!-- 指定执行myExecute方法 -->
            <result name="success">/result.jsp</result><!-- 结果为success时,跳转到result.jsp -->
        </action>
        <action name="userAction" class="com.shengsiyuan.struts2.UserAction">
            <result name="success">/output.jsp</result>
        </action>
        <action name="userAction2" class="com.shengsiyuan.struts2.UserAction2">
            <result name="success">/output.jsp</result>
        </action>
        <action name="register" class="com.shengsiyuan.struts2.RegisterAction">
            <result name="success">/registerResult.jsp</result>
            <result name="input">/register.jsp</result>
        </action>
    </package>
</struts>

截图
提交之前截图
提交之前截图

以html标签提交之后的截图
以html标签提交之后的截图

以struts标签,且未加theme=”simple”属性提交的结果
以struts标签,且未加theme="simple"属性提交的结果

若使用则呈现的结果同以html提交一样。不管是Action级别的,还是field级别的,在执行excute方法时,只要对应的错误容器中有错误消息,则转到input对应的页面,如果容器中没有错误消息,则继续往下执行。

在tomcat中手动添加web项目:

…………
<Context path="/myWebSite" docBase="D:/Workspace/servlet/myWebSite" reloadable="true"/>
</Host>
</Engine>
</Service>
</Server>
java.lang.ClassNotFoundException: javax.el.ELResolver错误解决方法:Remove of classpath server-api.jar and jsp-api.jar
…………

Action级别的自定义方法的输入校验

要点:

  1. Action中自定义方法的输入校验。对于通过action的method属性所指定的自定义方法,其对应的自定义输入校验方法名为validateMyExecute(假设自定义的方法名为 myExecute) 。底层是通过反射来调用的。

  2. 当在Action中指定了自定义的 execute方法时,首先会执行自定义的 execute 方法所对应的输入校验方法,然后再去执行标准的 validate 方法,执行完毕后如果出现了任何错误都不会再去执行自定义的execute方法,流程转向了 input这个名字所对应的页面上。

Login.jsp

…………
<body>
<form action="login.action">
    username: <input type="text" name="username"><br>
    password: <input type="password" name="password"><br>
    age: <input type="text" name="age"><br>
    date: <input type="text" name="date"><br>
    <input type="submit" value="submit">
</form>
</body>
</html>

LoginAction

package com.shengsiyuan.struts2;
import java.util.Date;
import com.opensymphony.xwork2.ActionSupport;
import com.shengsiyuan.exception.PasswordException;
import com.shengsiyuan.exception.UsernameException;
public class LoginAction extends ActionSupport
{
    private String username;
    private String password;
    private int age;
    private Date date;
    public Date getDate()
    {
        return date;
    }
    public void setDate(Date date)
    {
        this.date = date;
    }
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    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 execute() throws Exception
    {
        if(!"hello".equals(username))
        {
            throw new UsernameException("username invalid");
        }
        if(!"world".equals(password))
        {
            throw new PasswordException("password invalid");
        }
        return SUCCESS;
    }
    //自定的execute方法,在struts.xml中指定了LoginAction中的method方法为myExecute
    public String myExecute() throws Exception
    {
        System.out.println("myExecute invoked!!");
        return SUCCESS;
    }
    //Action自定义方法的输入校验
    public void validateMyExecute()    //此验证方法规定为将myExecute()方法名首字母大写后,再加前缀validate构成
    {
        System.out.println("validateMyExecute invoked!!");
        this.addActionError("action error");
    }
    @Override
    public void validate()
    {
        //System.out.println("validate invoked!");
        //this.addActionError("action error");
    }
}

注:如果去除了struts.xml文件中的method=”myExecute”,则对应的自定义的校验方法将无效。

<action name="login" class="com.shengsiyuan.struts2.LoginAction " method="myExecute">
    <result name="success">/result.jsp</result>
</action>

自定义Field级别的错误提示消息

要点:

1) 新建一个以 Action 名命名的 properties 文件 , 如RegisterAction.properties。 属性文件名要求为对应的Action文件名字。
2) 然后在该属性文件中指定每一个出错字段的错误消息 invalid.fieldvalue.birthday=birthday invalid !!

Tip: jdk提供的一款中文转换为unicode编码的工具,在jdk的bin目录下,native2ascii。在命令行下使用,使用方法如下:
native2ascii

struts2校验框架
要点:
Struts2的校验框架(有效的xml文件) 。具体来说分为字段优先校验器与校验器优先校验器。 配置文件的命名规则为:要对哪个Action加入校验框架,即在此个Action所在的包下添加一个名字为 xxxActioh-validation.xml文件。比如给RegisterAction用校验框架校验,则在RegiterAction对应的包下,添加一个名为RegisterAction-validation.xml的文件.
注释掉Action当中的校验方法与自定义的校验方法,配置Action的execute方法为默认的execute方法。 Struts2框架校验执行的先后顺序:
1) 首先执行校验框架(xml文件)
2) 执行自定义方法的校验方法(validateMyExecute)
3) 执行validate方法
对于struts.xml文件的结果配置来说,局部要优于全局。

xwork-validator.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
XWork Validators DTD.
Used the following DOCTYPE.
<!DOCTYPE validators PUBLIC
        "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
        "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
-->
<!ELEMENT validators (field|validator)+><!—字段优先与校验器优先两种-->
<!ELEMENT field (field-validator+)>
<!ATTLIST field
        name CDATA #REQUIRED
        >
<!ELEMENT field-validator (param*, message)>
<!ATTLIST field-validator
        type CDATA #REQUIRED
        short-circuit (true|false) "false"
        >
<!ELEMENT validator (param*, message)>
<!ATTLIST validator
        type CDATA #REQUIRED
        short-circuit (true|false) "false"
        >
<!ELEMENT param (#PCDATA)>
<!ATTLIST param
        name CDATA #REQUIRED
        >
<!ELEMENT message (#PCDATA)>
<!ATTLIST message
        key CDATA #IMPLIED
        >

RegisterAcion-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"><!-- 对username设置校验规则 -->
        <field-validator type="requiredstring"><!-- 字符串必填规则设置 -->
            <param name="trim">false</param><!-- 不清除空格 -->
            <message>username can't be blank!</message><!-- 当校验失败提示的错误消息 -->
        </field-validator>
        <field-validator type="stringlength"><!-- 字符串长度规则设置 -->
            <param name="minLength">4</param><!-- 最小长度 -->
            <param name="maxLength">6</param><!-- 最大长度 -->
            <param name="trim">false</param>
            <!—读取属性文件,使用国际化,当校验失败提示的错误消息 --
            <message key="username.invalid"></message>>
        </field-validator>
    </field>
    <field name="password">
        <field-validator type="requiredstring">
            <message>password can't be blank!</message>
        </field-validator>
        <field-validator type="stringlength">
            <param name="minLength">4</param>
            <param name="maxLength">6</param>
            <message>length of password should be between ${minLength} and ${maxLength}</message>
        </field-validator>
    </field>
    <field name="age">
        <field-validator type="required">
            <message>age can't be blank!</message>
        </field-validator>
        <field-validator type="int">
            <param name="min">10</param>
            <param name="max">40</param>
            <!—在这里可以用${元素名} 引用上面的子元素-->
            <message>age should be between ${min} and ${max}</message>
        </field-validator>
    </field>
    <field name="birthday">
        <field-validator type="required">
            <message>birthday can't be blank!</message>
        </field-validator>
        <field-validator type="date">
            <param name="min">2005-1-1</param>
            <param name="max">2007-12-31</param>
            <message>birthday should be between ${min} and ${max}</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">
<validator type="requiredstring">
    <param name="fieldName">username</param>
    <message>username can't be blank!</message>
</validator>
<validator type="stringlength">
    <param name="fieldName">username</param>
    <param name="minLength">4</param>
    <param name="maxLength">6</param>
    <!—在这里可以用${元素名} 引用上面的子元素-->
    <message>length of username should be between ${minLength} and ${maxLength}</message>
    </validator>
    <field name="birthday">
        <field-validator type="required">
            <message>birthday can't be blank!</message>
        </field-validator>
        <field-validator type="date">
            <param name="min">2005-1-1</param>
            <param name="max">2007-12-31</param>
            <message>birthday should be between ${min} and ${max}</message>
        </field-validator>
    </field>
</validators>

打开com.opensymphony.xwork2.validator.validators可以发现一个default.xml文件,打开可以发现其中定义了各种校验器,内容如下:
default.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
        "-//OpenSymphony Group//XWork Validator Config 1.0//EN"
        "http://www.opensymphony.com/xwork/xwork-validator-config-1.0.dtd">
<!-- START SNIPPET: validators-default -->
<validators>
    <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
    <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
    <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
    <validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/>
    <validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/>
    <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/>
    <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/>
    <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
    <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/>
    <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
    <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
    <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
    <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
    <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
    <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
    <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>
</validators>
        <!-- END SNIPPET: validators-default -->

错误消息国际化
在Action所在的包下建立两个属性文件,名字分别叫:
package_en_US.properties

username.invalid=username invalid

package_zh_CN.properties

username.invalid=\用\户\名\不\合\法\!
#用户名不合法

运行结果截图
运行结果截图

Struts2国际化
对于国际化的资源文件,其命名规则是: package_语言名_国家名,比如package_zh_CN,package_en_US 。

I18NTest1.java

package com.shengsiyuan.i18n;
import java.util.Locale;
public class I18NTest1
{
    public static void main(String[] args)
    {
        Locale[] locales = Locale.getAvailableLocales();
        for (Locale locale : locales)
        {
            System.out.println(locale.getDisplayCountry() + " : "
                    + locale.getCountry());        //显示国家及国家代码
//            System.out.println(locale.getDisplayLanguage() + " : " + locale.getLanguage());
        }
    }
}

I18Ntest2.java
在src目录(classpath)下,建立三个资源文件,分别如下(注 i18n即为资源文件的base name):
i18n_en_US.properties

hello=hello\:{0}

i18n_zh_CN.properties

hello=\你\好\:{0}

i18n.properties

hello=hello world

I18Ntest2.java代码如下 :

package com.shengsiyuan.i18n;
import java.util.Locale;
import java.util.ResourceBundle;
public class I18NTest2
{
    public static void main(String[] args)
    {
        System.out.println(Locale.getDefault());
        //绑定一个base name 为i18n的资源文件,Locale使用FRANCE(法国)
        ResourceBundle bundle = ResourceBundle.getBundle("i18n", Locale.FRANCE);
        //获取hello字符串的值
        String value = bundle.getString("hello");
        System.out.println(value);
    }
}

I18Ntest3.java(传入参数)

package com.shengsiyuan.i18n;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;
public class I18NTest3
{
    public static void main(String[] args)
    {
        Locale locale = Locale.CHINA;    //将locale定义为CHINA
        //绑定到资源文件
        ResourceBundle bundle = ResourceBundle.getBundle("i18n", locale);
        //获取资源文件中的字符串对象
        String value = bundle.getString("hello");
        //格式化字符串对象,并传入参数,后边的Object数组对象表示要插入到占位符上的具体的对象
        String result = MessageFormat.format(value, new Object[]{"维唯为为"});
        System.out.println(result);
    }
}

注意:调整浏览器的语言和字符编码选项后,要注意,程序中输入的日期格式,也要做相应的改变,不然会出现日期类型转换错误。字符串国际化的同时,也要注意,日期的国际化。

Struts2异常处理及全局异常与结果
要点:

我们既可以在 Action 中定义异常与结果,也可以定义全局的异常与结果,局部总是优于全局的,如果定义成全局,那么可以为所有的 Action 所公用,而局部的异常与结果只能被当前的 Action 所独享,不能为其他Action所共享。
validate主要进行没有业务逻辑的验证,比如判断用户名长名、密码与重复密码是否相同等。而判断用户登录是否成功;

struts2异常处理实例 在第三层建一个包,取名exception ,并在包中建两个类,分别取名为:
PasswordException.java

package com.shengsiyuan.exception;
public class UsernameException extends Exception
{
    private String message;
    public UsernameException(String message)
    {
        super(message);
        this.message = message;
    }
    public String getMessage()
    {
        return message;
    }
    public void setMessage(String message)
    {
        this.message = message;
    }
}

UsernameException.java

package com.shengsiyuan.exception;
public class PasswordException extends Exception
{
    private String message;
    public PasswordException(String message)
    {
        super(message);
        this.message = message;
    }
    public String getMessage()
    {
        return message;
    }
    public void setMessage(String message)
    {
        this.message = message;
    }
}

编辑loginAction的execute方法

    public String execute() throws Exception
    {
        if(!"hello".equals(username))
        {
            throw new UsernameException("username invalid");
        }
        if(!"world".equals(password))
        {
            throw new PasswordException("password invalid");
        }
        return SUCCESS;
    }

编辑struts.xml文件
Struts2的处理局部异常机制

…………
<struts>
    <package name="struts2" extends="struts-default">
    <action name="login" class="com.shengsiyuan.struts2.LoginAction">
        <exception-mapping result="usernameInvalid" exception="com.shengsiyuan.exception.UsernameException"></exception-mapping>
        <exception-mapping result="passwordInvalid" exception="com.shengsiyuan.exception.PasswordException"></exception-mapping>
        <result name="usernameInvalid">/usernameInvalid.jsp</result>
        <result name="passwordInvalid">/passwordInvalid.jsp</result>
        <result name="success">/result.jsp</result>
    </action>
    ………………
</struts>

Struts2的处理全局异常机制

…………
<struts>
    <package name="struts2" extends="struts-default">
    <global-results>
        <result name="usernameInvalid">/usernameInvalid.jsp</result>
        <result name="passwordInvalid">/passwordInvalid.jsp</result>
    </global-results>
    <global-exception-mappings>
        <exception-mapping result="usernameInvalid" exception="com.shengsiyuan.exception.UsernameException"></exception-mapping>
        <exception-mapping result="passwordInvalid" exception="com.shengsiyuan.exception.PasswordException"></exception-mapping>
    </global-exception-mappings>
    ………………
</struts>

版权所有,转载请注明出处 luowei.github.io.