Struts1.x学习笔记
<!--[if !supportLists]-->l <!--[endif]-->各种action的基本配置与使用
<!--
struts1.x的action配置都很简单,
继承自Action的自定义Action配置如下,
当要进生输入校验时必设置validate为true,
input 指定验证出错后要返回的页面
-->
<action path="/register" type="com.kettas.action.UserAction"
input="/register.jsp" validate="true" name="userForm">
<forward name="success" path="/result.jsp"></forward>
</action>
<!--
继承自MappingDispatcherAction,多了一个parameter属性,
此属性指定本此请求调用此action中的哪个方法。
-->
<action path="/updateorder" type="com.kettas.action.OrderAction"
parameter="updateOrder">
<forward name="success" path="/result.jsp"></forward>
</action>
<!--
继承自DispatcherAction跟继承自MappingDispatcherAction配置相同,不过
parameter的含义不同,它后面指定的值要在请求此action的url后面加上,如一个删除订单的请求应该为
http://localhost/shopcart/deleteorder.do?method=deleteOrder 其中method后面的值为要调用action
的方法名。
-->
<action path="/deleteorder" type="com.kettas.action.OrderAction"
parameter="method">
<forward name="success" path="/result.jsp"></forward>
</action>
<!--
LookupDispatcherAction与DispatcherAction的配置相同,但是含义不同
它的parameter指的是页面按提交按钮的name值。并且按键的value必须要有资源文件的支持,且在其action中
指定这个值与action内的方法的对应关系。很麻烦,由此可见这个LookupDispatcherAction实在不常用!
-->
<action path="/usermanage" type="com.kettas.action.UserLookupAction" parameter="callmethod">
<forward name="success" path="/ok.jsp"></forward>
</action>
<!--[if !supportLists]-->l <!--[endif]-->异常处理
Strut1.x的异常处理蛮简单。配置时分为全局,异常与局部异常。
对于一个action若发生异常则先查找本身action是否有相应异常的配置,若无则查找全局异常配置,还没有则报错。异常处理一定会用到资源文件
<!--[if !supportLists]-->1. <!--[endif]-->局部配置如下
<!--
key指定信息在资源文件中的键值
type指发生的异常类型。
bundle使用哪个资源文件,不配置则使用默认的资源文件
-->
<exception key="user.register" type="com.kettas.exception.UserRegisterException" bundle="exception" path="/error.jsp"/>
<!--[if !supportLists]-->2. <!--[endif]-->全局配置如下,各属性与局部配置一样
<global-exceptions>
<exception key="user.register" type="com.kettas.exception.UserRegisterException" bundle="exception" path="/error.jsp"/>
</global-exceptions>
3.对于一个异常,通过会转入另外一个页面,提示错误,struts1.x在此设计有些不合理,一旦转入异常提示页面时,即使这个页面设置了isErrorPage="true"但是它的内置有exception对象依然为空,所以不能用exception得到异常中的信息,只能用<html:errors />来得到,一大失误哦,这也就是说struts1.x的异常处理是离不开资源文件的,因为异常的信息只能放在资源文件中。
<!--[if !supportLists]-->l <!--[endif]-->国际化
国际化要对就资源文件,这必须在struts-config.xml中指定,与struts2中则不需。
配置方法:
<message-resources parameter="com.kettas.resources.messages" ></message-resources>
<message-resources parameter="com.kettas.resources.information" key="information" ></message-resources>
其中第一种配置为默认配置,没有指定其key属性,则struts会自动将其key设为org.apache.struts.action.MESSAGE,源码可查,且一旦要使用资源文件则一定要有一个不能有key属性,即一定要给org.apache.struts.action.MESSAGE指定一个资源文件,struts默认此资源文件为全局资源文件,即以后使用资源文件的地方若没有指定其使用哪个文件,则会从这个默认配置资源文件中找。Struts会将资源文件打包MessageResourcesConfig以key值为键放在ServletContext的作用域中,在其它使用到资源文件的地方可以key值指定使用哪个资源文件中的键值对;
类型转换
<!--[if !supportLists]-->1. <!--[endif]-->struts的页面的国际化
使用<bean:message bundle="information" key="errors.validwhen" arg0="param1" arg1="param2"/>
Bundle指定是使用哪个资源文件,即在资源文件配置时指定的key值。
Key为资源文件中的键值。
Arg0,arg1指若资源文件中有占位符时用来替代的值。
<!--[if !supportLists]-->2. <!--[endif]-->struts的异常的国际化
<!--
key指定信息在资源文件中的键值
type指发生的异常类型。
bundle使用哪个资源文件,不配置则使用默认的资源文件
-->
<exception key="user.register" type="com.kettas.exception.UserRegisterException" bundle="exception" path="/error.jsp"/>
<!--[if !supportLists]-->3. <!--[endif]-->struts的输入验证的国际化
配置:
<field property="userName" depends="required">
<!-- msg的key值为资源文件中键值,name指定这个消息为哪个验证器指定,
因为一个field可以同时指定几个验证器。
bundle指定使用哪个资源文件,这个值在资源文件配置时被指定为key值。
arg用来代替信息中的点为符
-->
<msg key="errors.required" name="required" bundle="information"/>
<arg position="0" key="userName" resource="false" />
</field>
<!--[if !supportLists]-->l <!--[endif]-->输入验证
一.使用验证框架
(输入验证时需要用到如下两个jar包commons-validator.jar 与oro.jar.)
注意:要进行框架验证的Form必须继承自ValidatorForm,ValidatorActionForm。以及使用动态的
DynaValidatorForm和DynaValidatorActionForm。其中ValidatorForm与DynaValidatorForm在验证文件的配置时相同,另外两个相同。
前两个匹配action配置中的name,而后两个匹配的是action配置中的path.由此可知两者在使用上的一些局限性。
<!--[if !supportLists]-->1. <!--[endif]-->框架的基本配置
Struts的验证框架默认是并不打开的,以插件的形式运行,在使用之前要进行插件注册,注册配置为
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames"
value="/org/apache/struts/validator/validator-rules.xml,
/WEB-INF/validation.xml" />
</plug-in>
其set-property的property属性的值可以在源码找到,这个配置可以到struts提供的示例程序中去拷。对于不同版本的struts1.x它的validator-rules.xml放置的位置可能并不太一样,这点不能硬搞。 另外validation.xml为验证文件。
<!--[if !supportLists]-->2. <!--[endif]-->验证文件基本配置:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE form-validation PUBLIC
"-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.3.0//EN"
"http://jakarta.apache.org/commons/dtds/validator_1_3_0.dtd">
<form-validation>
<formset>
<form name="userForm">
<!--
验证文件的配置
-->
<field property="userName" depends="required,maxlength">
<!-- msg的key值为资源文件中键值,name指定这个消息为哪个验证器指定,
因为一个field可以同时指定几个验证器,用逗号分开。
bundle指定使用哪个资源文件,这个值在资源文件配置时被指定为key值。
arg用来代替信息中的点为符,其position确定占位符的位置,key确定占位符的值,若
此值来自资源文件还要指定来自哪个资源文件,用bundle指定。若不是则要显示指出
其resource为false,此时它的key值会直接代替占位符.另外如下所示,它还可以引用定义的变量。
另外arg还有name属性指定它为哪个msg生成占位信息。
-->
<msg key="errors.required" name="required" bundle="information"/>
<msg key="errors.maxlength" name="maxlength" bundle="information"/>
<arg position="0" key="userName" resource="false" />
<arg position="1" key="${var:maxlength}" resource="false"/>
<var>
<!-- 为什么叫maxlength ,可以在这个验证器的实现类中找到 -->
<var-name>maxlength</var-name>
<var-value>10</var-value>
</var>
</field>
<field property="userPwd" depends="required">
<msg key="errors.required" name="required" />
<arg position="0" key="userPwd" resource="false" />
</field>
<!-- 下面的这个验证很有用,可以用来验证两个表单是否相等
它有个texst参数,只有这个值为真时才能通过验证。其写法比较固定。
*this*指本字段,与别的字段比较则它其名字,实事上是form的属性
-->
<field property="reUserPwd" depends="validwhen">
<arg position="0" key="repassword" resource="false"/>
<arg position="1" key="password" resource="false"/>
<msg name="validwhen" key="errors.validwhen"/>
<var>
<var-name>test</var-name>
<var-value>(*this*==userPwd)</var-value>
</var>
</field>
<!--
-->
<field property="birthday" depends="required,date">
<msg key="errors.required" name="required" />
<msg key="errors.typeinvalid" name="date" />
<arg position="0" key="birthday" resource="false" />
<var>
<var-name>datePattern</var-name>
<var-value>YYYY-MM-DD</var-value>
</var>
</field>
</form>
</formset>
</form-validation>
二,重载ActionForm的validate方法
1.这种方法很简单,有一点是往ActionErrors里面加入ActionMessage时ActionMessage的重载构造方法可以确定传递进去的字符串是否来自资源文件以及在资源文件中的键值,若不是来自资源文件则此键值将会被显示,它不能指定这个键值来自哪个资源文件,这要靠页面标签来确定,这种设计明显不些不合理但也很无奈。而ActionErrors的add方法指定这个信息的标识符,即页面上用struts的标签以此标识来显示错误信息。
2.当Validate验证出错时会返回到action指定的input页面。出错的标志为ActionErrors里面是否有ActionMessage.
3.在页面上可以用struts标签得到错误信息。
<html:errors property="username.required" bundle="validateerrors"/>
Property指定在ActionErrors的add方法指定的标识符,bundle指定ActionErrors添加的ActionMessage的键值来自哪个资源文件。
4.要注意框架验证与重载ActionForm的validate方法的验证不能同时使用,若同时存在则只执行validate验证。
<!--[if !supportLists]-->l <!--[endif]-->动态表单的使用
<!--[if !supportLists]-->1. <!--[endif]-->基本使用
基本配置
<!--
若要进行表单验证则要使用用DynaValidatorForm或者DynaValidatorActionFrom.
前者在Validation.xml中匹配的是action的name属性,即可以认为在validation.xml中
对此form进行验证,而后者匹配的是action的path属性。即对action进行验证,由此可见两的适用范围。
若不必要进行验证,则只要使用最简单的DynaActionForm就可以了!
-->
<form-bean name="productForm" type="org.apache.struts.validator.DynaValidatorForm">
<form-property name="productName" type="java.lang.String"></form-property>
<form-property name="productPrice" type="java.lang.Double"></form-property>
<form-property name="productDate" type="java.util.Date"></form-property>
<form-property name="discription" type="java.lang.String"></form-property>
</form-bean>
<!-- -
在action中,要取出Dyna...FromKnow的值必须要用其get方法,值入参数值,实际上,Dyna...From里面就是将这
此值保存在一个Map中。另外要注意的是,在页面取值是,继承自ActionForm的表单取值很简单,但是继承动态表单的则不同,它必须先得到Map,然后才能得到其值。
Eg:productForm.map.productName 由此也可知Map在页面用el表达示取值的方法。
-->
<!--[if !supportLists]-->2. <!--[endif]-->输入验证
基本配置
它的配置与普通表的ActionForm一样,没有任何区别。这就不多说了!
<!--[if !supportLists]-->l <!--[endif]-->Struts1.x的上传与下载
<!--[if !supportLists]-->1. <!--[endif]-->文件上传
<!-- form的配置,很简单,但是很必要,好像只能用动态表单。另外不定数量的文件上传并不太好解决 -->
<form-bean name="photoForm"
type="org.apache.struts.validator.DynaValidatorForm">
<form-property name="photo1"
type="org.apache.struts.upload.FormFile">
</form-property>
<form-property name="photo2"
type="org.apache.struts.upload.FormFile">
</form-property>
<form-property name="photo3"
type="org.apache.struts.upload.FormFile">
</form-property>
</form-bean>
------------------------------------------------------------------
<!-- 跟普通的action的配置没有任何区别 -->
<action path="/upload" type="com.kettas.action.UploadAction"
name="photoForm">
<forward name="success" path="/ok.jsp"></forward>
</action>
------------------------------------------------------------------------------
<form action="<%=request.getContextPath() %>/upload.do" method="post" enctype="multipart/form-data">
<input type="file" name="photo1">
<input type="file" name="photo2">
<input type="file" name="photo3">
<input type="submit">
</form>
------------------------------------------------------------------
package com.kettas.action;
import java.io.FileOutputStream;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.*;
import org.apache.struts.upload.FormFile;
import org.apache.struts.upload.MultipartRequestHandler;
public class UploadAction extends Action {
@SuppressWarnings("unchecked")
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
DynaActionForm daf=(DynaActionForm)form;
MultipartRequestHandler handler=daf.getMultipartRequestHandler();
Hashtable photos=handler.getFileElements();
Set set=photos.keySet();
System.out.println(set.size());
Iterator values=set.iterator();
while(values.hasNext())
{
FormFile file=(FormFile) photos.get(values.next());
System.out.println(file.getFileName());
FileOutputStream out=new FileOutputStream("c:\\"+file.getFileName());
out.write(file.getFileData());
out.close();
}
return mapping.findForward("success");
}
}
<!--[if !supportLists]-->2. <!--[endif]-->文件下载
文件下载功能是通过DownloadAction实现的。
继承DownloadAction。
实现getStreamInfo方法,返回StreamInfo对象。
部分代码的实现:
package com.kettas.action;
import java.io.File;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import org.apache.struts.actions.DownloadAction;
public class TestDownLoad extends DownloadAction{
@Override
/*
* 这个类可以没有execute方法。因为通过链接下载文件成功成没有必要跳转,当然也可以
* 做到,这时文件下载将会被抑制,就没有下载的意义了。
* 它的配置与其它的action没有任何区别
*/
protected StreamInfo getStreamInfo(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception {
String fileName = "test.txt";
response.setHeader("Content-disposition",
"attachment; filename=" + fileName);
File file = new File(this.servlet.getServletContext().getRealPath("/upload")+"//"+fileName);
return new FileStreamInfo("text/html", file);
}
}
<!--[if !supportLists]-->l <!--[endif]-->Struts1.x的其它一些问题
<!--[if !supportLists]-->1. <!--[endif]-->时间类型先set验证时出现问题
查了很多资料,基本上没有好的解决办法,struts给的示例中用动态表单来接收数据,而接收时间的字段也是字符串类型,这样就避免了转换,并且可以进行格式验证。当这个动态表单数据传入action时,按需要再将这个字符串转成时间类型。由此也算是struts的ActionForm的一个硬伤。对于没有时间类型的form表单用静态actionform可以,若有则最好用动态。
其实其本上所有的表单提交用动态都行。推荐这么做
<!--[if !supportLists]-->2. <!--[endif]-->一些有用的技巧。
对于form表达的提交按钮,如果这个按钮有name属性,则这个按钮的值也会被提交,若则不会,struts的LookupDispatcherAction就是以此来实现的。Struts内置了个小东西,若是一个submit的name值org.apache.struts.action.CANCEL则在执行业务方法前调用Action的isCancelled方法可以确定是否点了这个按钮以此来写此相应代码!当然这些实现很简单,自已也可以实现!
<!--[if !supportLists]-->l <!--[endif]-->Struts1.x的防止表单的重复提交
Struts的令牌机制
在转入提交表单的action中要调用saveToken方法,这样将会在session中放入令牌,
并将生成的令牌放入表单提交页面,一般放在一个隐藏域中,用el表达式给其符值,且隐藏域的名称为固定的
org.apache.struts.action.TOKEN 这可以从Globals.class中找到。
在处理表单提交的action中要调用isTokenValid方法来判断是否是第一次提交。
把表单处理后调用resetToken(request);将session中的令牌重置(其实这个重置似乎并没有太大意义)。
由上面的可知,可以自己来实现一个令牌使用
<!--[if !supportLists]-->l <!--[endif]-->Struts的titles框架
<!--[if !supportLists]-->1. <!--[endif]-->在页面直接使用titles标签
先引入标签:<%@taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
将模板页面要代替的内容用标签占位:<tiles:insert attribute="content"></tiles:insert>
在另外一页面将模板页与内容页整合:
<tiles:insert page="usertemplate.jsp">
<tiles:put name="content" value="zczzh.jsp"></tiles:put>
</tiles:insert>
<!--[if !supportLists]-->2. <!--[endif]-->使用titles框架
先插入框架的插件
<!-- 使用tiles框架则要将它的插件插入,当在页面用tiles标签时,则不必要加入些插件,
事实上直接使用tiles标签也蛮简单,只是多了一个页面
,但是它可以使用其它的自定义action,各人所好吧。
参数相注意,就这么写-->
<plug-in className="org.apache.struts.tiles.TilesPlugin">
<set-property property="definitions-config"
value="/WEB-INF/tiles-defs.xml" />
<set-property property="definitions-parser-validate"
value="true" />
</plug-in>
定义titles配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE component-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN" "tiles-config_1_1.dtd" >
<component-definitions>
<!--
可以配置一个模板页面,然后其它页面从它来继承,如下:
它的其中一个空位置并没有被填充
-->
<definition name="template" page="/template.jsp">
<put name="head" value="head.jsp" ></put>
<put name="foot" value="foot.jsp" ></put>
<put name="left" value="left.jsp" ></put>
</definition>
<!--
这个页面继承了模板页面,填充了它没有填充的地方。
当然如果模板页面已经填充,它可以覆盖。
-->
<definition name="register" extends="template">
<put name="content" value="content2.jsp"></put>
</definition>
</component-definitions>
在struts的action中调用
<!-- 使用tiles,则自定义的action必须继承ForwardAction,其它跟别的自定义action一样,不过
它的parameter属性用来指定tiles配置文件中definition标签的name,当两者相同时如果execute方法返回值为null,
则页面转向parameter指定的组合页面。否则到相应的页面,蛮有用的 -->
<action path="/testtiles" type="com.kettas.action.MyTilesAction"
parameter="register">
<forward name="success" path="/index.jsp"></forward>
</action>