概述
一个健壮的 web 应用程序必须确保用户输入是合法、有效的.
Struts2 的输入验证
–基于 XWork Validation Framework 的声明式验证:Struts2 提供了一些基于 XWork Validation Framework 的内建验证程序. 使用这些验证程序不需要编程, 只要在一个 XML 文件里对验证程序应该如何工作作出声明就可以了. 需要声明的内容包括:
对哪个 Action 或 Model 的那个字段进行验证
使用什么验证规则
在验证失败时应该把什么样的出错消息发送到浏览器端
–编程验证:通过编写代码来验证用户输入
声明式验证的 helloworld
I. 先明确对哪一个 Action 的哪一个字段进行验证: age
II. 编写配置文件:
> 把 struts-2.3.15.3\apps\struts2-blank\WEB-INF\classes\example 下的 Login-validation.xml 文件复制到当前 Action 所在的包下.
> 把该配置文件改为: 把 Login 改为当前 Action 的名字.
> 编写验证规则: 参见 struts-2.3.15.3/docs/WW/docs/validation.html 文档即可.
> 在配置文件中可以定义错误消息:
<field name="age">
<field-validator type="int">
<param name="min">20</param>
<param name="max">50</param>
<message>^^Age needs to be between ${min} and ${max}</message>
</field-validator>
</field>
> 该错误消息可以国际化吗. 可以 <message key="error.int"></message>.
再在 国际化资源文件 中加入一个键值对: error.int=^^^Age needs to be between ${min} and ${max}
III. 若验证失败, 则转向 input 的那个 result. 所以需要配置 name=input 的 result
<result name="input">/validation.jsp</result>
IV. 如何显示错误消息呢 ?
> 若使用的是非 simple, 则自动显示错误消息.
> 若使用的是 simple 主题, 则需要 s:fielderror 标签或直接使用 EL 表达式(使用 OGNL)
${fieldErrors.age[0] }
OR
<s:fielderror fieldName="age"></s:fielderror>*
若一个 Action 类可以应答多个 action 请求, 多个 action 请求使用不同的验证规则, 怎么办 ?
> 为每一个不同的 action 请求定义其对应的验证文件: ActionClassName-AliasName-validation.xml
> 不带别名的配置文件: ActionClassName-validation.xml 中的验证规则依然会发生作用. 可以把各个 action 公有的验证规则配置在其中. 但需要注意的是, 只适用于某一个 action 的请求的验证规则就不要这里再配置了.
Struts2 内建的验证规则
required: 确保某给定字段的值不是空值 null
requiredstring: 确保某给定字段的值既不是空值 null, 也不是空白.
–trim 参数. 默认为 true, 表示 struts 在验证该字段值之前先剔除前后空格.
stringlength: 验证一个非空的字段值是不是有足够的长度.
–minLength: 相关字段的最小长度. 若没有给出这个参数, 该字段将没有最小长度限制
–maxLength:相关字段的最大长度. 若没有给出这个参数, 该字段将没有最大长度限制
–trim: 在验证之前是否去除前后空格
date: 确保某给定日期字段的值落在一个给定的范围内
–max:相关字段的最大值. 若没给出这个参数, 该字段将没有最大值限制
–min:相关字段的最小值. 若没给出这个参数, 该字段将没有最小值限制
email: 检查给定 String 值是否是一个合法的 email
url: 检查给定 String 值是否是一个合法的 url
regex: 检查某给定字段的值是否与一个给定的正则表达式模式相匹配.
–expresssion*: 用来匹配的正则表达式
–caseSensitive: 是否区分字母的大小写. 默认为 true
–trim: 是否去除前后空格. 默认为 true
int: 检查给定整数字段值是否在某一个范围内
–min: 相关字段的最小值. 若没给出这个参数, 该字段将没有最小值限制
–max: 相关字段的最大值. 若没给出这个参数, 该字段将没有最大值限制
conversion: 检查对给定 Action 属性进行的类型转换是否会导致一个转换错误. 该验证程序还可以在默认的类型转换消息的基础上添加一条自定义的消息
expression 和 fieldexpression: 用来验证给定字段是否满足一个 OGNL 表达式.
–前者是一个非字段验证程序, 后者是一个字段验证程序.
–前者在验证失败时将生成一个 action 错误, 而后者在验证失败时会生成一个字段错误
–expression*: 用来进行验证的 OGNL 表达式
声明式验证框架的原理:
> Struts2 默认的拦截器栈中提供了一个 validation 拦截器
> 每个具体的验证规则都会对应具体的一个验证器. 有一个配置文件把验证规则名称和验证器关联起来了. 而实际上验证的是那个验证器. 该文件位于 com.opensymphony.xwork2.validator.validators 下的 default.xml
<validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
短路验证:
若对一个字段使用多个验证器, 默认情况下会执行所有的验证. 若希望前面的验证器验证没有通过, 后面的就不再验证, 可以使用短路验证
<!-- 设置短路验证: 若当前验证没有通过, 则不再进行下面的验证 -->
<field-validator type="conversion" short-circuit="true">
<message>^Conversion Error Occurred</message>
</field-validator> <field-validator type="int">
<param name="min">20</param>
<param name="max">60</param>
<message key="error.int"></message>
</field-validator>
若类型转换失败, 默认情况下还会执行后面的拦截器, 还会进行 验证. 可以通过修改 ConversionErrorInterceptor 源代码的方式使当类型转换失败时, 不再执行后续的验证拦截器, 而直接返回 input 的 result
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
ValidationAware va = (ValidationAware) action; if(va.hasFieldErrors() || va.hasActionErrors()){
return "input";
}
}
关于非字段验证: 不是针对于某一个字段的验证.
<validator type="expression">
<param name="expression"><![CDATA[password==password2]]></param>
<message>Password is not equals to password2</message>
</validator>
显示非字段验证的错误消息, 使用 s:actionerror 标签: <s:actionerror/>
不同的字段使用同样的验证规则, 而且使用同样的响应消息 ?
error.int=${getText(fieldName)} needs to be between ${min} and ${max} age=\u5E74\u9F84
count=\u6570\u91CF
自定义验证器:
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. 使用: 和目前的验证器一样.
IV. 示例代码: 自定义一个 18 位身份证验证器
public class IDCard {
final int[] wi = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1 };
final int[] vi = { 1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2 };
private int[] ai = new int[18]; public IDCard() {} public boolean Verify(String idcard) {
if (idcard.length() == 15) {
idcard = uptoeighteen(idcard);
}
if (idcard.length() != 18) {
return false;
}
String verify = idcard.substring(17, 18);
if (verify.equals(getVerify(idcard))) {
return true;
}
return false;
} public String getVerify(String eightcardid) {
int remaining = 0; if (eightcardid.length() == 18) {
eightcardid = eightcardid.substring(0, 17);
} if (eightcardid.length() == 17) {
int sum = 0;
for (int i = 0; i < 17; i++) {
String k = eightcardid.substring(i, i + 1);
ai[i] = Integer.parseInt(k);
} for (int i = 0; i < 17; i++) {
sum = sum + wi[i] * ai[i];
}
remaining = sum % 11;
} return remaining == 2 ? "X" : String.valueOf(vi[remaining]);
} public String uptoeighteen(String fifteencardid) {
String eightcardid = fifteencardid.substring(0, 6);
eightcardid = eightcardid + "19";
eightcardid = eightcardid + fifteencardid.substring(6, 15);
eightcardid = eightcardid + getVerify(eightcardid);
return eightcardid;
} public static void main(String[] args) { String idcard1 = "350211197607142059";
String idcard2 = "350211197607442059"; IDCard idcard = new IDCard();
System.out.println(idcard.Verify(idcard1));
System.out.println(idcard.Verify(idcard2));
} }
IDCard.java
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);
}
}
}
IDCardValidator