最近参与一个项目,使用Struts作为web层框架。在开发过程中,有大量比如CRUD之类的业务,使用相同的ActionForm,因此在处理这些业务时使用DispatchAction是非常方便的,而且代码也集中在一个类中被多个方法分割。因此,比较容易进行修改和维护。
但是当同时结合Apache的Validation框架使用时,就会出现问题。如果在配置Action时没有关闭Validation那么所有的form在构造或重新利用时都将进行验证,比如:在显示增加界面是不需要进行Form验证,在提交增加时不需要进行Form验证。另外,增加时和编辑时的Input是不同,但是Action配置时只能提供一个Input元素。
针对以上问题我的处理方法是扩展Struts,首先我想做的是让Struts根据配置来进行验证,也就是说对需要进行Form验证的方法使用验证,在验证不成功时根据每个方法的配置返回不同的Input。对不需要进行Form的方法不使用验证。打开Struts(1.2.9)的代码,在RequestProcessor类中有这样一个方法:
HttpServletResponse response,
ActionForm form,
ActionMapping mapping)
这个方法是在创建(或者从新利用)Form之后进行Form验证的方法,我们可以复写这个方法,对于需要进行验证的DispatchAction中的方法我们不进行处理,由RequestProcessor进行处理,对于不需要进行Form验证的方法我们默认返回验证成功。以下是我继承RequestProcessor后的新类:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.InvalidCancelException;
import org.apache.struts.action.RequestProcessor;
/** */ /**
* @author haixing 2006-9-25
*
* 您可以任意使用和修改这些代码.
*/
public class DispatchRequestProcessor extends RequestProcessor ... {
protected boolean processValidate(HttpServletRequest request,
HttpServletResponse response,
ActionForm form,
ActionMapping mapping)
throws IOException, ServletException, InvalidCancelException ...{
DispatchActionMapping bam = null;
if (mapping instanceof DispatchActionMapping) ...{
bam = (DispatchActionMapping)mapping;
}
else ...{
return super.processValidate(request, response, form, mapping);
}
if (mapping.getParameter() == null) ...{
return super.processValidate(request, response, form, mapping);
}
String parameter = request.getParameter(mapping.getParameter());
if (parameter == null) ...{
return super.processValidate(request, response, form, bam);
}
parameter = parameter.trim().toLowerCase();
if (bam.containsIgnore(parameter)) ...{
return true;
}
else ...{
bam.setNoIgnore(parameter);
}
return super.processValidate(request, response, form, bam);
}
}
这里使用到了一个DispatchActionMapping类,这个类是作为配置支持的,大家知道ActionMapping(也就是后续版本中的ActionConfig)是用来配置Action的,因此,为了达到我的目的需要扩展ActionMapping:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;
import org.apache.struts.action.ActionMapping;
/** */ /**
* @author haixing 2006-9-25
*
* 您可以任意使用和修改这些代码.
*
* Struts的ActionMapping的扩展,主要是为BaseAction服务(BaseAction继承了org.apache.struts.actions.DispatchAction)
* 所以这个类也可以用在DispathAction上。
*
* 作用:
* 1. 可以指定不需要进行Form验证的Action方法 (通过DispatchAction的paratement指定的参数)
* 2. 可以指定需要进行Form验证的Action方法以及对应的Input
*
* 配置方法(struts-config.xml):
* 1. <action-mappings type="haixing.BaseActionMapping">
* 2. <action classname="haixing.BaseActionMapping">
*
* 这个类需要配合使用BaseRequestProcessor,那么就需要在配置文件(struts-config.xml)中进行如下配置:
* <controller processorClass="haixing.BaseRequestProcessor"/>
*
* Action的写法:
* <action path="/news"
* name="newsForm"
* type="haixing.NewsAction"
* input="/default_input"
* scope="request">
* <set-property property="ignores" value="addShow,delete,modifyShow"/>
* <set-property property="inputs" value="add=/news/addNews.jsp, modify=/news/modifyNews.jsp"/>
* <forward name="list" path="/news/listNewsForManagement.do"/>
* <forward name="addShow" path="/news/addNews.jsp"/>
* <forward name="modifyShow" path="/news/modifyNews.jsp"/>
* <forward name="list" path="/news/listNewsForManagement.do"/>
* </action>
*
* <set-property property="ignores" value="addShow,delete,modifyShow"/>
* 这个配置指定:addShow, delete, modifyShow时不需要进行Form验证,那么其他的所有方法都将进新Form验证。
*
* <set-property property="inputs" value="add=/news/addNews.jsp, modify=/news/modifyNews.jsp"/>
* 指定add方法的input是/news/addNews.jsp,modify方法的Input是/news/modifyNews.jsp。如果在Action中的其他方法没有在这里指
* 定,那么都将默认返回action中的input。
*
* action标签中的input为默认的input,不知道为什么ActionMapping是默认保存在Session中
*
*/
public class DispatchActionMapping extends ActionMapping ... {
private static final long serialVersionUID = -4695833433866065225L;
/** *//**
* 不需要进行Form验证的方法名称列表
*/
private ArrayList ignores = new ArrayList();
/** *//**
* 需要进行Form验证的方法名称以及Input的map
*/
private HashMap inputs = new HashMap();
/** *//**
* 当前需要验证的方法名
*/
private String currentNoIgnore = null;
/** *//**
* 设置当前需要验证的方法名
*
* @param parameter
*/
public void setNoIgnore(String parameter) ...{
currentNoIgnore = parameter;
}
/** *//**
* 设置需要验证的方法名以及Input
* 各个名称之间以","分割,名称与input之间以"="分割
*
* @param inputs
*/
public void setInputs(String inputs) ...{
StringTokenizer st = new StringTokenizer(inputs, ",");
while (st.hasMoreTokens()) ...{
String input = st.nextToken().trim();
//如果去掉空格后字符串长度为0,那么跳过
if (input.length() > 0)
parseInput(input);
}
}
/** *//**
* 解析方法名及input,以"="分割
* @param input
*/
protected void parseInput(String input) ...{
StringTokenizer st = new StringTokenizer(input, "=");
//方法名,默认为null
String key = null;
//名称默认为null
String forward = null;
//因为传递过来的字符窜长度大于0,所以这个方法不会失效
key = st.nextToken().toLowerCase();
//如果没有写input,那么input为null
if (st.hasMoreElements())
forward = st.nextToken().trim();
inputs.put(key, forward);
}
/** *//**
* 设置需要忽略的方法名
* @param ignores
*/
public void setIgnores(String ignores) ...{
StringTokenizer st = new StringTokenizer(ignores, ",");
while (st.hasMoreTokens()) ...{
String ignore = st.nextToken().trim().toLowerCase();
if (ignore.length() > 0)
this.ignores.add(ignore);
}
}
/** *//**
* 当前请求参数中是否包含要被忽略的方法名
*
* @param ignore
* @return
*/
public boolean containsIgnore(String ignore) ...{
return ignores.contains(ignore);
}
/** *//**
* 返回input的名称
* @see org.apache.struts.config.ActionConfig#getInput()
*/
public String getInput() ...{
//如果没有传递参数,那么返回默认的input
if (currentNoIgnore == null)
return super.getInput();
Object input = inputs.get(currentNoIgnore);
if (input != null)
return (String)input;
else
return super.getInput();
}
}
具体的我就不多说了,请看上面代码的注释。这样扩展后使得在使用DispathAction是可以配置多个Input,并且可以指定不需要进行验证的方法。上面的代码可能写得不够完善,留待日后再做吧。