国际化和输入式子校验
消息处理与国际化
配置全局国际化
1.在struts.xml中配置全局国际化资源文件
<constant name="struts.custom.i18n.resources" value="i18n"></constant>
配置的文件名,根据需要自己命名。
2.在src文件下面创建配置文件
i18n.properties文件配置如下:
username=UserName
password=Password
submit=Submit
time=Time:{0}
i18n_en_US.properties文件配置如下:
username=username
password=password
submit=submit
time=Time:{0}
i18n_zh_CN.properties文件配置如下:
username=用户名
password=密码
submit=提交
time=时间:{0}
3.输出页面配置
<s:form action="">
<s:textfield name="username" key="username"></s:textfield>
<s:password name="password" key="password"></s:password>
<s:submit key="submit"></s:submit>
</s:form>
还可以使用类DefaultTextProvider的实例来获取
<s:form action="">
<s:textfield name="username" label="%{getText('username')}"></s:textfield>
<s:password name="password" label="%{getText('password')}"></s:password>
<s:submit key="submit"></s:submit>
</s:form>
如果页面的主题是simple,配置代码如下:
使用<s:text name=""/>
标签访问国际化资源文件里的Value值
<s:form action="" theme="simple">
<s:text name="username"/><s:textfield name="username" key="username"></s:textfield>
<br>
<s:text name="password"/><s:password name="password" key="password"></s:password>
<br>
<s:submit key="submit" value="%{getText('submit')}"></s:submit>
</s:form>
4.访问值栈里的属性值
1)编写action实现类
public class TestI18nAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private Date date=null;
public String execute() {
date=new Date();
String time=getText("time",Arrays.asList(date));
System.out.println(time);
return "success";
}
/*必须设置改方法,否则页面数据无法显示*/
public Date getDate() {
return date;
}
}
2)访问资源配置:
<action name="i18n" class="com.atguigu.struts2.i18n.app.TestI18nAction">
<result>/i18n.jsp</result>
<result name="input">/i18n.jsp</result>
</action>
3)页面获取配置:
<s:text name="time">
<s:param value="date"></s:param>
</s:text>
也可以在国际化资源文件中做如下配置:
time=Time:${date}
页面获取设置:
<s:text name="time"></s:text>
Action 资源文件范围
在Action类所在的路径建立名为ActionName_language_country.properties 的文件。
包范围资源文件
在包的根路径下建立文件名为package_language_country.properties的属性文件,一旦建立,处于该包下的所有 Action 都可以访问该资源文件。注意:包范围资源文件的 baseName 就是package,不是Action所在的包名。
加载资源文件的顺序
假设我们在某个 ChildAction 中调用了getText(“username”):
(1)加载和 ChildAction 的类文件在同一个包下的系列资源文件 ChildAction.properties
(2)加载 ChildAction 实现的接口 IChild,且和 IChildn 在同一个包下 IChild.properties 系列资源文件。
(3)加载 ChildAction 父类 Parent,且和 Parent 在同一个包下的 baseName 为 Parent.properties 系列资源文件。
(4) 若 ChildAction 实现 ModelDriven 接口,则对于getModel()方法返回的model 对象,重新执行第(1)步操作。
(5) 查找当前包下 package.properties 系列资源文件。
(6) 沿着当前包上溯,直到最顶层包来查找 package.properties 的系列资源文件。
(7) 查找 struts.custom.i18n.resources 常量指定 baseName 的系列资源文件。
(8) 直接输出该key的字符串值。
通过超链接切换语言
Struts2 使用 i18n 拦截器处理国际化,并且将其注册在默认的拦截器中。
i18n 拦截器的处理过程
(1)先从request_locale参数上获取值,如果request_locale为空,从request_only_locale中获取值。
(2)根据上面两个参数创建Locale
(3)获取session,如果session不为空:获取上面创建的locale,locale为空则创建session对应的locale(如果session中没有会从浏览器中获取创建)。locael保存到session中(request_locale中获取需要保存)
(4)保存到invocationContext里面
选择输入示例代码:
<a href="i18n.action?request_locale=en_US">English</a>
<a href="i18n.action?request_locale=zh_CN">中文</a>
<a href="index.jsp">Index Page</a>
输入验证
声明式验证(字段验证)过程
1.创建Action类
public class TestValidationAction extends ActionSupport{
/**
* 省去get、set和无参构造器
*/
private static final long serialVersionUID = 1L;
private int age;
public String execute() {
System.out.println("age:"+age);
return "success";
}
}
2.创建输入页面
<s:form action="testValidation.action">
<s:textfield name="age" label="Age"></s:textfield>
<s:submit></s:submit>
</s:form>
3.配置struts.xml
<action name="testValidation" class="com.atguigu.struts2.validation.app.TestValidationAction" method="execute">
<result>/success.jsp</result>
<result name="input">/index.jsp</result>
</action>
4.配置验证规则
文件名称必须配置正确,否则配置规则将无法生效,和action在同一个包下面
文件名为:TestValidationAction-validation.xml
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.2//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<field name="age">
<field-validator type="int">
<param name="min">20</param>
<param name="max">60</param>
<message>Age needs to be between ${min} and ${max}</message>
</field-validator>
</field>
</validators>
5.运行页面展示
通过struts.xml中的/index.jsp类确定显示
若是simple主题显示配置:
<s:form action="testValidation.action" theme="simple">
Age:<s:textfield name="age" label="Age"></s:textfield>
<!-- ${fieldErrors.age[0]}-->
<s:fielderror fieldName="age"></s:fielderror>
<s:submit></s:submit>
</s:form>
6.国际化配置
<validators>
<field name="age">
<field-validator type="int">
<param name="min">20</param>
<param name="max">60</param>
<message key="error.int"></message>
</field-validator>
</field>
</validators>
在国际化文件中配置:
error.int=Age needs to be between ${min} and ${max}
记得在struts.xml中添加如下一条国际化配置:
<constant name="struts.custom.i18n.resources" value="i18n"></constant>
7.若一个 Action 类的多个 action 使用不同的验证规则
ActionClass-action的name属性-validation.xml, 例如 UserAction-User_create-validation.xml,ActionClass-validation.xml中的验证规则依然发生作用,可以把各个action公有的验证规则配置在其中。
声明式验证原理
Struts2 的 Validation 拦截器负责加载和执行已注册的验证程序,它是 defaultStack 拦截器的一员。
Struts2 默认的拦截器栈中提供了一个 validation 拦截器
每个具体的验证规则都会对应具体的一个验证器. 有一个配置文件把验证规则名称和验证器关联起来了. 而实际上验证的是那个验证器.
配置文件位于 com.opensymphony.xwork2.validator.validators 下的 default.xml
<validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
Struts2 内建的验证程序
conversion
检查对给定 Action 属性进行的类型转换是否会导致一个转换错误。该验证程序还可以在默认的类型转换消息的基础上添加一条自定义的消息
<field name="age">
<field-validator type="conversion">
<message>Conversion Error Ocuurred</message>
</field-validator>
<field-validator type="int">
<param name="min">20</param>
<param name="max">60</param>
<message key="error.int"></message>
</field-validator>
</field>
当输入类型转化错误的时候,报错:
•Invalid field value for field “age”.
•Conversion Error Ocuurred
•输入年龄必须在 20 和 60之间
设置短路验证
若对一个字段使用多个验证器, 默认情况下会执行所有的验证。 若希望前面的验证器验证没有通过,后面的就不再验证,,可以使用短路验证。
<validator …/> 元素和 <field-validator …/> 元素可以指定一个可选的 short-circuit 属性,该属性指定该验证器是否是短验证器,默认值为 false。
对同一个字段内的多个验证器,如果一个短路验证器验证失败,其他验证器不会继续校验。
1.
<field name="age">
<field-validator type="conversion" short-circuit="true">
<message>Conversion Error Ocuurred</message>
</field-validator>
<field-validator type="int">
<param name="min">20</param>
<param name="max">60</param>
<message key="error.int"></message>
</field-validator>
</field>
当输入类型转化错误的时候,报错:
•Invalid field value for field “age”.
•Conversion Error Ocuurred
2. param拦截器进行类型转换,失败处理是由conversionError拦截器(StrutsConversionErrorInterceptor)进行处理的,若类型转换失败,默认情况下还会执行后面的拦截器,还会进行验证。可以通过修改 ConversionErrorInterceptor 源代码中的intercept方法的方式使当类型转换失败时,不再执行后续的验证拦截器, 而直接返回 input 的 result。
重写类的步骤:
1.在src下面新建类所在的包:com.opensymhony.xwork2.interceptor
2.在包下面创建对应的类ConversionErrorInterceptor
3.找到方法intercept,在回调ActionInvocation之前添加如下代码:
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
ValidationAware va = (ValidationAware) action;
if(va.hasFieldErrors() || va.hasActionErrors()){
return "input";
}
}
4.在conversionError拦截器中就报错结束了,进而没有执行后续的拦截器,包括validation拦截器。
expression
用来验证给定字段是否满足一个 OGNL 表达式。
配置过程:
1.页面输出配置
<s:form action="testValidation.action" theme="simple">
Age:<s:textfield name="age" label="Age"></s:textfield>
<s:fielderror fieldName="age"></s:fielderror>
Password:<s:password name="password"></s:password>
Password2:<s:password name="password2"></s:password>
<s:actionerror/>
<s:submit></s:submit>
</s:form>
2.在TestValidationAction-validation.xml中设置校验配置
<validator type="expression">
<param name="expression"><![CDATA[password==password2]]></param>
<message>Password is not equals to password2</message>
</validator>
3.action配置
public class TestValidationAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private int age;
private String password;
private String password2;
/*省略构造器,get和set显示*/
public TestValidationAction(int age) {
super();
this.age = age;
}
public String execute() {
System.out.println("age:"+age);
return "success";
}
}
4.设置显示
<s:actionerror/>
错误信息的重用性
1.配置拦截器
<field name="age">
<field-validator type="conversion" short-circuit="true">
<message>Conversion Error Ocuurred</message>
</field-validator>
<field-validator type="int">
<param name="min">20</param>
<param name="max">60</param>
<message key="error.int"></message>
</field-validator>
</field>
<field name="count">
<field-validator type="int">
<param name="min">20</param>
<param name="max">50</param>
<message key="error.int"></message>
</field-validator>
</field>
2.配置国际化文件
error.int=${getText(fieldName)} needs to be between ${min} and ${max}
age=Age
count=Count
name=Name
password=passWord
password2=passWord2
自定义验证器
I. 定义一个验证器的类
自定义的验证器都需要实现 Validator.
可以选择继承 ValidatorSupport 或 FieldValidatorSupport 类
若希望实现一个一般的验证器, 则可以继承 ValidatorSupport
若希望实现一个字段验证器, 则可以继承 FieldValidatorSupport
具体实现可以参考目前已经有的验证器.
若验证程序需要接受一个输入参数, 需要为这个参数增加一个相应的属性
II. 在配置文件中配置验证器
默认情况下下, Struts2 会在 类路径的根目录下加载 validators.xml 文件. 在该文件中加载验证器.
该文件的定义方式参见默认验证器配置文件: 位于 com.opensymphony.xwork2.validator.validators 下的 default.xml
若类路径下没有指定的验证器, 则从 com.opensymphony.xwork2.validator.validators 下的 default.xml 中的验证器加载
III. 使用: 和目前的验证器一样
示例:
1.创建对应的类
2.自定义一个验证器
public class IDCardValidator extends FieldValidatorSupport {
@Override
public void validate(Object object) throws ValidationException {
//1. 获取字段的名字和值
String fieldName = getFieldName();
Object value = this.getFieldValue(fieldName, object);
//2. 验证
IDCard idCard = new IDCard();
boolean result = idCard.Verify((String)value);
//3. 若验证失败, 则 ...
if(!result){
addFieldError(fieldName, object);
}
}
}
3.在需要调用的页面中添加参数
idCard:<s:textfield name="idcard"></s:textfield>
<s:fielderror fieldName="idcard"></s:fielderror>
4.在action中添加属性
5.配置文件validators.xml
如果此文件配置有问题,将导致所有配置拦截失效。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator Definition 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">
<!-- START SNIPPET: validators-default -->
<validators>
<validator name="idcard" class="com.atguigu.struts2.validation.app.IDCardValidator"/>
</validators>
6.在拦截器配置文件中配置拦截器
<field name="idcard">
<field-validator type="idcard">
<message>It is not a idcard</message>
</field-validator>
</field>