基于XML配置方式实现输入校验
I:定义Action
* 要继承ActionSupport或者实现Validateable接口:
II:配置struts_validate.xml文件
* 验证出错转向的页面
struts.xml配置<result name=“input”>/validate/loginxml.jsp</result>
其中input转向是在action中已经定义好的.
III:配置验证的xml文件
* 验证的xml文件的规范在xwork-core-2.1.6.jar包下的:xwork-validator-1.0.3.dtd
* 验证文件的命名
* 在这个校验文件中,对action中字符串类型的username属性进行验证,
首先要求调用trim()方法去掉空格,然后判断用户名是否为空。
* 该文件需要和action类放在同一个包下,文件的取名应遵守
ActionClassName-validation.xml规则,其中ActionClassName为action
的简单类名,-validation为固定写法。
* 例如:如果Action类为ValidateXmlAction. 那么该文件
的取名应为:ValidateXmlAction-validation.xml
* ValidateXmlAction-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-validator type= "requiredstring">
<param name= "trim">true</param >
<message key= "error.required"></message >
</field-validator>
<field-validator type= "email">
如果需要国际化,可以为message
指定key属性,key的值为属性文件中的key。
<message key= "error.email"></message >
</field-validator>
</validators>
<validators>: 根元素
<field>:指定action中要校验的属性,name属性指定将被验证的表单字段的名字
<field-validator>:指定校验器, type 指定验证规则
上面指定的校验器requiredstring是由系统提供的,系统提供了能满足大部分验证需求
的校验器,这些校验器的定义可以在xwork-2.x.jar中的
com.opensymphony.xwork2.validator.validators下的default.xml中找到。
<param>:子元素可以向验证程序传递参数
<message>:子元素为校验失败后的提示信息,如果需要国际化,可以为message
指定key属性,key的值为属性文件中的key。
校验器案例:
equired 必填校验器
<field-validator type="required">
<message>性别不能为空!</message>
</field-validator>
requiredstring 必填字符串校验器
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>用户名不能为空!</message>
</field-validator>
stringlength:字符串长度校验器
<field-validator type="stringlength">
<param name="maxLength">10</param>
<param name="minLength">2</param>
<param name="trim">true</param>
<message><![CDATA[产品名称应在2-10个字符之间]]></message>
</field-validator>
nt:整数校验器
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>年龄必须在1-150之间</message>
</field-validator>
字段OGNL表达式校验器
<field name="imagefile">
<field-validator type="fieldexpression">
<param name="expression"><![CDATA[imagefile.length() <= 0]]></param>
<message>文件不能为空</message>
</field-validator>
</field>
email:邮件地址校验器
<field-validator type="email">
<message>电子邮件地址无效</message>
</field-validator>
regex:正则表达式校验器
<field-validator type="regex">
<param name="expression"><![CDATA[^13\d{9}$]]></param>
<message>手机号格式不正确!</message>
</field-validator>
2:基于XML配置方式对指定action方法实现输入校验
当校验文件的取名为ActionClassName-validation.xml时,会对 action中的所有处理方法实施输入验证。如果你只需要对action中的某个action方法实施校验,那么,校验文件的取名应为:ActionClassName-ActionName-validation.xml,其中ActionName为struts.xml中action的名称。例如:在实际应用中,常有以下配置:
<action name="user_*" class="cn.itcast.action.UserAction" method="{1}">
<result name="success">/WEB-INF/page/message.jsp</result>
<result name="input">/WEB-INF/page/addUser.jsp</result>
</action>
UserAction中有以下两个处理方法:
public String add() throws Exception{
....
}
public String update() throws Exception{
....
}
要对add()方法实施验证,校验文件的取名为: UserAction-user_add-validation.xml
要对update()方法实施验证,校验文件的取名为: UserAction-user_update-validation.xml
基于XML校验的一些特点
当为某个action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml两种规则的校验文件时,系统按下面顺序寻找校验文件:
1。AconClassName-validation.xml
2。ActionClassName-ActionName-validation.xml
系统寻找到第一个校验文件时还会继续搜索后面的校验文件,当搜索到所有校验文件时,会把校验文件里的所有校验规则汇总,然后全部应用于处理方法的校验。如果两个校验文件中指定的校验规则冲突,则只使用后面文件中的校验规则。
当action继承了另一个action,父类action的校验文件会先被搜索到。假设UserAction继承BaseAction, UserAction在struts.xml的配置如下:
<action name="user" class="cn.itcast.action.UserAction" method="{1}">
.....
</action>
访问上面名为user的action,系统先搜索到BaseAction-validation.xml, BaseAction-user-validation.xml,接着搜索到UserAction-validation.xml, UserAction-user-validation.xml。校验规则是这四个文件的总和。
分析验证原理:
在xwork-core-2.1.6.jar包下包含一个XML验证文件如下
<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>
也就是说当我们在配置文件中写好的配置 系统会自动去找对应的类方法进行验证
补充理解小点:
I:系统加载验证的XML文件 读取到DefaultActionValidatorManager类中的ValidatorConfig进行使用
II:<result name="input">/WEB-INF/page/addUser.jsp</result>在struts2中这个input怎么返回的。
查看源码:
在ValidationInterceptor 验证拦截器查看得知:
public class ValidationInterceptor extends MethodFilterInterceptor {
private boolean alwaysInvokeValidate = true;
public void setAlwaysInvokeValidate(String alwaysInvokeValidate) {
this.alwaysInvokeValidate = Boolean.parseBoolean(alwaysInvokeValidate);
}
protected void doBeforeInvocation(ActionInvocation invocation) throws Exception {
if (action instanceof Validateable && programmatic) {
Validateable validateable = (Validateable) action;
try {
//把验证错误信息添加在fieldErrors Map集合中 但并没有返回 “input”
PrefixMethodInvocationUtil.invokePrefixMethod(
invocation,
new String[] { VALIDATE_PREFIX, ALT_VALIDATE_PREFIX });
}
//这个默认为true
if (alwaysInvokeValidate ) {
//以上是把验证错误信息放入到一个fieldErrors Map集合中后继续执行
validateable.validate();
}
}
}
protected String doIntercept(ActionInvocation invocation) throws Exception {
//方法执行完成 继续执行下一个拦截器 DefaultWorkflowInterceptor
doBeforeInvocation(invocation);
return invocation.invoke();
}
}
在DefaultWorkflowInterceptor拦截器中可以查看源码得知input返回值在这里进行返回
public class DefaultWorkflowInterceptor extends MethodFilterInterceptor {
private static final long serialVersionUID = 7563014655616490865L;
private static final Logger LOG = LoggerFactory.getLogger(DefaultWorkflowInterceptor .class );
private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
private String inputResultName = Action.INPUT;
public void setInputResultName(String inputResultName) {
this.inputResultName = inputResultName;
}
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
//首先盘对请求的action是否实现了 ValidationAware的接口
if (action instanceof ValidationAware) {
ValidationAware validationAwareAction = (ValidationAware) action;
//这一步 判断fieldErrors Map集合中是否有错误
if (validationAwareAction.hasErrors()) {
//如果Map集合不为空就赋值resultName 为input
String resultName = inputResultName;
if (action instanceof ValidationWorkflowAware) {
resultName = ((ValidationWorkflowAware) action).getInputResultName();
}
if (annotation != null) {
if (!annotation.methodName().equals("" )) {
Method method = action.getClass().getMethod(annotation.methodName());
resultName = (String) method.invoke(action);
} else {
resultName = annotation.resultName();
}
}
//在这里返回 input;
return resultName;
}
}
//如果Map集合 为空就继续执行
return invocation.invoke();
}
}
//DefaultWorkflowInterceptor类继承自MethodFilterInterceptor 实现了doIntercept方法
//当doIntercept返回input时进行接收
public abstract class MethodFilterInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
if (applyInterceptor(invocation)) {
//接收到 input 进行向上返回
return doIntercept(invocation);
}
return invocation.invoke();
}
protected abstract String doIntercept(ActionInvocation invocation) throws Exception;
}
//DefaultActionInvocation 管理调度拦截器的执行和返回值(个人理解)
public class DefaultActionInvocation implements ActionInvocation {
public String invoke() throws Exception {
String profileKey = "invoke: ";
try {
UtilTimerStack.push(profileKey);
//执行拦截器
if (interceptors .hasNext()) {
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack. push(interceptorMsg);
try {
//在这里进行最终的接受拦截器的返回值 “input”
resultCode =interceptor.getInterceptor().intercept(DefaultActionInvocation. this);
}
finally {
UtilTimerStack. pop(interceptorMsg);
}
} else {
resultCode = invokeActionOnly();
}
if (proxy .getExecuteResult()) {
//执行<result name="input">/WEB-INF/page/addUser.jsp</result>
executeResult();
}
executed = true;
}
return resultCode ;
}
finally {
UtilTimerStack. pop(profileKey);
}
}
private void executeResult() throws Exception {
result = createResult();
// getResultCode也就是获取到了input
String timerKey = "executeResult: " + getResultCode();
try {
UtilTimerStack. push(timerKey);
if (result != null) {
result.execute( this);
}
}
}
}
以上都是个人单步调试看到的 能力有限 仅限个人理解