Reference :《Struts2 输入校验 实例讲解》《ST2.NO.0006 struts2.0 官网文档学习笔记之六 - Form Validation》
Struts的信息校验方式有两种
1.硬编码的方式:重写ActionSupport类的validate()方法。
2.采用Struts2的输入校验框架,即采用XML配置的方式进行校验。
硬编码方式
Register.jsp
<s:form action="register" theme="simple"><font color="red" size="2"><s:fielderror /></font><table border="1" ><tr><td>UserName:</td><td><s:textfield name="person.userName" label="usernmae"/>6-10 cahr</td></tr><tr><td>Password:</td><td><s:password name="person.password" label="password"/></td></tr><tr><td>Re-password:</td><td><s:password name="person.repassword" label="repassword" /></td></tr><tr><td>Age:</td><td><s:textfield name="person.age" label="age" />0-150</td></tr><tr><td>Birthday:</td><td><s:textfield name="person.birthday" label="birthday"/>must be date</td></tr><tr><td><s:submit /></td></tr></table></s:form>
RegisterAction.java
public void validate() {if (null == person.getUsername() || person.getUsername().length() < 6|| person.getUsername().length() > 10) {this.addFieldError("username", "username is invalid");}if (null == person.getPassword() || person.getPassword().length() < 6|| person.getPassword().length() > 10) {this.addFieldError("password", "password is invalid!");} else if (null == person.getRepassword()|| person.getRepassword().length() < 6|| person.getRepassword().length() > 10) {this.addFieldError("password", "password is invalid!");} else if (!person.getRepassword().equals(person.getRepassword())) {this.addFieldError("password", "tow password is not be same!");}if (person.getAge() <= 0 || person.getAge() > 150) {
this.addFieldError("age", "age is invalid!");}}
Struts.xml
<action name="register" class="test.RegisterAction"><result name="success">/test/Thankyou.jsp</result><result name="input">/test/Register.jsp</result></action>
<result name="input">标签是必须添加的,Struts2在验证不成功的情况下会自动return这个input出来,所以一定要在Struts.xml里面指定一个jsp页面来接着!否则会报404错误哦~
注意:1.personBean中定义的age和birthday分别是int型和Date型,所以在填写表单时候如果值不能转换为对应的类型,Struts2会首先报错。
如图:验证失败并且 Struts2 返回 "input", Struts2 框架将重新显示 register.jsp 页面. 因为我们使用了 Struts2 form 标签, 所以Struts2 会自动的添加这些错误消息.这些错误的消息来自我们在 调用addFieldError() 方法中设置的错误消息之一. 这个 addFieldError 方法有两个参数. 第一个是表单的属性名称来说明哪一个属性输入有误, 另一个是需要在Form表单属性上面显示的错误消息.
2.之后才会到RegisterAction里面去找valide()方法进行其他方面的验证。
原理分析:
关键在于继承的ActionSupport类,ActionSupport类里面有个
private final ValidationAwareSupport validationAware = new ValidationAwareSupport();
我们在validate()里面调用的addFieldError实际上就是调用的ValidationAwareSupport 类里面的setFieldErrors方法。下面是ValidationAwareSupport 的部分代码:
public class ValidationAwareSupport implements ValidationAware, Serializable {private Collection<String> actionErrors;//保存Action级别的错误信息 Collectionprivate Map<String, List<String>> fieldErrors; //保存Field级别的错误信息 Map//添加一条字段错误的详细代码
public synchronized void addFieldError(String fieldName, String errorMessage) {//获取所有字段错误的Map
final Map<String, List<String>> errors = internalGetFieldErrors();
//取出对应字段的错误信息列表
List<String> thisFieldErrors = errors.get(fieldName);//如果之前没有错误,新创建一个List保存错误信息
if (thisFieldErrors == null) {thisFieldErrors = new ArrayList<String>();
errors.put(fieldName, thisFieldErrors);}//将错误信息添加到列表中
thisFieldErrors.add(errorMessage);}//初始化存储错误的信息的Map
private Map<String, List<String>> internalGetFieldErrors() {
if (fieldErrors == null) {fieldErrors = new LinkedHashMap<String, List<String>>();
}return fieldErrors;
}//检查是否有错误
public synchronized boolean hasErrors() {return (hasActionErrors() || hasFieldErrors());
}}
注意到ValidationAwareSupport 类里面还有一个actionErrors的Collection,其实Struts2中的错误有两种级别:
1. Action级别的错误
2. Field 级别的错误
而Struts2只有判断到两种错误级别的信息都没有的情况,才认为是输入校验通过。
validate代码能跑在execute之前要归功于Struts2的默认拦截器DefaultWorkflowInterceptor,部分代码如下:
public class DefaultWorkflowInterceptor extends MethodFilterInterceptor {private String inputResultName = Action.INPUT; //INPUT哦/**
* Intercept {@link ActionInvocation} and returns a <code>inputResultName</code>* when action / field errors is found registered.** @return String result name*/@Overrideprotected String doIntercept(ActionInvocation invocation) throws Exception {Object action = invocation.getAction();if (action instanceof ValidationAware) {ValidationAware validationAwareAction = (ValidationAware) action;//如果validate没通过
if (validationAwareAction.hasErrors()) {
if (LOG.isDebugEnabled()) {
LOG.debug("Errors on action " + validationAwareAction + ", returning result name 'input'");}String resultName = inputResultName; //input哦!!
//需要action实现ValidationWorkflowAware才能返回true,我们的例子返回的是false
if (action instanceof ValidationWorkflowAware) {resultName = ((ValidationWorkflowAware) action).getInputResultName();}//好像是某种可配置的annotation,在我们的例子里显然是null啦
InputConfig annotation = action.getClass().getMethod(invocation.getProxy().getMethod(), EMPTY_CLASS_ARRAY).getAnnotation(InputConfig.class);
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;
}}//不出意外的话这里invoke的是execute()方法嗯,我们的例子没能到这一步
return invocation.invoke();
}}
一些零碎的知识:
这样的话validate方法还是运行在registerPeople之前的,可是如果有有一个情景要求在一个registerAction里面为两种不同的角色进行注册和验证的时候怎么办呢?可以把execute方法和validate方法留空,分别指定两个执行方法如:registerA()和registerB(),然后再这两个方法中显示调用各自的validate方法,类似于:<action name="register" class="test.RegisterAction"method="registerPeople"><result name="success">/test/Thankyou.jsp</result><result name="input">/test/Register.jsp</result></action>
public String registerPeople() {
validateRegister();/*do some thing, such as DB storage */
return SUCCESS;
}
关于采用XML配置的校验方式,偶还木有学……标记一下Struts2官网的教程,以后用空了去研究一下!
如有理解错误,敬请指正!