在用
struts
框架开发的
Web
应用中,一般习惯为数据库中的每一
Action
建一个
ActionForm
,每一张表队员几个
Action
。比如一个用户注册的简单应用中,可能有这样一些操作:新注册一个账户,修改现有账户以及删除一个账户。(对应数据库表的三种基本操作)。
最简单的做法是:分别写三个
Action
来处理他们,每个
Action
对应一个
ActionForm
。这么做比较简单,但会导致大量的类,而且对应一张表的
ActionForm
一般相同(可能有些字段没有)。因此在
struts
自己带的
struts-example
中使用了一个
RegistrationForm.java
对应了好几个
Action
。另外为了减少
JSP
的数量,它利用了
<logic:Present>
标签判断
RegistrationForm
中的属性
action
。这样根据不同的
action
的取值来显示几个(例子是两个)大同小异的
JSP
页面。
但即使这样,每一张表的操作仍然对应好几个
action
。因此
struts
中提供了
DispatchAction
[org.apache.struts.actions.DispatchAction]
来减少这些相关业务逻辑的
action
的个数,以便于系统的开发与维护。它的使用方法是:
1,
写一个
Action
继承
DispatchAtion
。如果是用
JBuilder
等工具,记得去掉自动给加上的
execute()
方法,因为
struts
会先调用此方法,如果没有的话它才会查找
action mapping
中
的
parameter
属性,根据这个属性用
Reflection
调用相应的方法。如果没找到
parameter
指定的方法,则会出错。
JBuilder
会生成一个抛出异常的
execute
方法,而我们开发最初可能不会实现这个方法,因此习惯
return
一个
null
,然后我们实现了
parameter
指定的方法,结果弄了半天也调不通。
2.
在
struts-config.xml
文件里进行
action mapping
时在
action
元素中加入一个
parameter
属性,用来指明不同参数时使用不同的方法,习惯将
parameter
取
method
,事实上取别的值也可以。如果
ActionForm
里有个请求参数的也叫
method
,那么就会引起错误。
Action[/newAccountAction] does not contain method named de
上面就是我在请求的
jsp
中添加一个
name
为
method
的文本输入框时的错误(我在其中输入的时
de
)。
另外,许多与更新数据库相关的业务逻辑会需要验证,在没有
validator
包
[org.apache.struts.validator]
之前只能在
ActionForm
里用
validate()
方法校验数据。如果客户端也需要验证的话,
JSP
程序员还要自己写
javascript
代码,这可是件很头痛的事情,因为一般的
Java IDE
都没有调试
javascript
的功能,有时弄了半天发现是
document.formname
弄错了。在引入了
validator plugin
之后,一切变得简单了,要进行一些基本的验证(类型,
email
等)只需在
validation.xml
中说明一些就可以了,也不用写
ActionForm
的
validate
()方法了。
使用方法为:
1.
写一个类继承
ValidatorForm[org.apache.struts.validator.ValidatorForm]
,如果想使用动态的
FormBean
的话修改
struts-config.xml
的
form
元素。
2
.在
validation.xml
中加入
<form>
元素。如
struts
自带的
struts-validator
例子中使用了:
<form name="registrationForm">
<field property="firstName"
depends="required,mask,minlength">
<arg0 key="registrationForm.firstname.displayname"/>
<arg1 name="minlength" key="${var:minlength}" resource="false"/>
<var>
<var-name>mask</var-name>
<var-value>^/w+$</var-value>
</var>
<var>
<var-name>minlength</var-name>
<var-value>5</var-value>
</var>
</field>
<!—more fields are omitted here
à
</form>
它的意思是:逻辑名为
registrationForm
(在
struts-config.xml
的
<form-bean>
中定义)的
ValidatorForm
的
firstName
属性需要
3
个验证:必填,输入内容是一个或多个字符,最少
5
个字符。有关
field
的属性和子元素请参考
struts
参考文档。正则表达式请参考
ORO
和
Regexp
,它们都是
jakarta
的项目。
在上面的
form
元素有一个必需的属性
name
,它指明了需要验证的
ActionForm
的逻辑名,如果一个
ActionForm
对应一个
Action
,这当然没有问题。但如果一个
ActionForm
想被多个
Action
使用,而且这些
Action
需要的验证是不同的,例如新注册一个用户需要验证,而查看已注册用户的信息就不能验证。因此
struts
又提供了一个
ValidatorActionForm
,它的特点是:它不是根据
form
的逻辑名,而是根据
action
的路径来验证不同的
actionform
,因为在
action mapping
时会用
name
属性指定这个
action
的
actionform
。这样一个
actionform
就可以可选择的被或不被验证了。
我们的目标是:对应一张表的操作,只有一个
Action
类(或其子类)和一个
ActionForm
类(或其子类)。因此,可以选择使用
DispatchAction
来处理所有相关的业务逻辑。而
ActionForm
可以是
ValidatorForm
或
ValidatorActionForm
。我在最初的想法是:使用
ValidatorActionForm
。这样,可以在
validation.xml
中将
form
元素的
name
属性指定为一个
action
的
path
,但发现这样用的话就没有办法使用客户端验证了。客户端验证要在
<html:javascript formName=””>
的
formName
属性指定
formName
(
ActionForm
的逻辑名),我试着把
struts1.1
的
validator
例子的
RegistrationForm
改成
ValidatorActionForm
的子类,发现它根本每验证,而且在
jsp
中把
javascript
直接显示出来了。将
formName
属性改成那个
Action
的
path
也不管用,不知有没有别的办法实现(不能修改
Validator
类)。
即使不考虑客户端验证,也遇到一个难题:提交给一个
Action
的操作,有的需要验证(如新注册一个用户),有的不需要(如查看用户信息)。如果不作任何处理的话,则只能要么全验证,要么全不验证,这显然是不行的。当时我就想,要是
Validator
能根据请求
action
后面的参数来决定是否验证就好了。比如我在
validation.xml
中使用
<form name="/registerAction?method=AddUser">
<field property="userName"
depends="required">
<arg0 key="register.label.userName"/>
</field>
</form>
如果我的请求是
registerAction?method=AddUser
,它就验证,而如果是
registerAction?method=ViewUser
,它就不验证。于是我把
validator
包的源码加入工程,发现这个
validation.xml
中的内容都读到
ValidatorResource
这个类的一个实例中了,在验证时从这个类的实例查找相应的
formbean
,例如我在
<form name=”registerForm”>
的话,它在
ValidatorPlugIn
的
init()
方法(被
ActionServlet
的
init()
调用)时会把
name=registerForm
保存在
ValidatorPlugIn
的一个
ValidatorResource
的变量里。如果客户请求一个
action
,当然会生成一个
ActionForm
(或是已存在的),如果这个
ActionForm
的逻辑名是
registerForm
,并且这个
ActionForm
的父类的
validate()
被调用(这个
ActionForm
没有
validate()
方法,或者在
validate
()方法中通过
super.validate()
),那么
ValidatorForm
会通过这个
ValidatorResource
找到要验证的
ActionForm
的逻辑名,而
ValidatorActionForm
则先通过
path
找到一个
Action
,再通过
action mapping
的
name
属性找到要验证的
ActionForm
。
因此我用
<form name="/registerAction?method=AddUser">
,则它自然找不到一个
action
的
path
是
/registerAction?method=AddUser
,因此没有任何验证。看来这么是不行的。
那有没有别的办法呢?
DispatchAction
能根据不同的参数选择不同的方法,那就应该能根据不同的参数选择不同的验证。因此我只要重写
validate
方法,如果参数是
viewUser
就不验证(
return null
),否则调用父类的
validate
()。不过用这种方法有一个问题,那就是不能使用动态的
ActionForm
,即
DynaValidatorForm
,因为它根本没有这个类,不可能写
validate()
方法。
public ActionErrors validate(ActionMapping actionMapping, HttpServletRequest httpServletRequest) {
/**@todo: finish this method, this is just the skeleton.*/
String parameter=actionMapping.getParameter();
String paramValue=httpServletRequest.getParameter(parameter);
System.out.println("from actionmapping "+paramValue);
parameter=httpServletRequest.getParameter("method");
System.out.println("from request "+parameter);
if(paramValue.equals("ViewUser")){
return null;
}
ActionErrors errors=super.validate(actionMapping,httpServletRequest);
return errors;
}
测试一下,还好,都正常。绕了半天弯路,在
struts
源码中设了无数断点,却发现有这么简单的实现方法。不过也学到不少东西。对开源项目,我们总是抱怨文档太少,其实源代码就是最好的文档。