Strtus2 OGNL表达式的结合
描述: struts2为OGNL表达式准备了两个对象
ActionContext: 作为ognl表达式的Context
valueStack: 作为ognl表达式的Root
以上两个对象的创建
都是strutsPrepareAndExecuteFilter中准备好.
Ognl和Struts使用上的结合:
-
表单提交,其中提交的键可以看作是ognl表达式
Action中有User对象,我们想直接将表单参数提交到User对象中封装,做法:
1>提交的参数的键=> user.name 就会在值栈中查找名为user的对象,并赋值到该对象的name属性
2>使用ModelDriven,我们的Action在getModel方法中将User对象返回.ModelDriven拦截器会将我们返回的User对象放入值栈中(栈顶),那么 在表单中直接提交name,将会把name值装入栈顶的user对象的name属性 -
文件下载中文件名乱码问题解决(配置文件中使用OGNL)
application/zip
is
attachment;filename=“${fileName}”
10240
-
<s:debug>标签介绍
-
Struts2标签中OGNL表达式(<s:properties>)
Struts2标签
打开struts-2.3.7\docs\WW\tag-reference.html可以看到Struts2提供的所有标签。其中分为“通用标签”和“UI标签”两大部分。我们不可能把所有标签都介绍到,我们只会介绍其中比较有用的几个标签介绍一下。
1 Struts2通用标签之数据标签
<s:property>(重要)
<s:property>标签用来执行OGNL表达式,最为常用的方式是使用它在页面中输出ValueStack或ActionContext中的数据。
<s:property value=”#request.hello”/>,等于ActionContext.getContext().getRequest().get(“hello”)。
名称 必需 默认值 类型 说明
default 否 无 String 表示默认值。当结果为null时,输出默认值。
escape 否 true boolean 表示是否进行转义。该值为true时,当结果中包含<、>、”、’、&时,对其进行转义。
value 否 栈顶对象 Object Ognl表达式
<s:set>
<s:set>标签用来创建一个变量,保存到指定的范围内。
<s:set var=”myVar” value=”#parameters.score[0]” scope=”page”/>,创建一个变量,保存到page范围,key为myVar,值为“#parameters.score[0]”的运算结果。
名称 必需 默认值 类型 说明
var 是 无 String 变量的域名字,默认不是Ognl表达式
value 否 栈顶对象 Object Ognl表达式。
scope 否 Action String 变量的范围。可选值为:application、session、request、page、action
scope的可选值中的action是我们陌生的范围,它是scope的默认值。它表示保存到request和OgnlContext两个范围中。即比request范围多出了一个OgnlContext范围。
<s:set var=”myVar” value=”#parameters.score[0]” />
<s:property value=”#myVar”/>等同于ActionContext.getContext().get(“myVar”);
<s:property value=”#request.myVar”/>等同于ActionContext.getContext.getReuqest().get(“myVar”);
<s:push>
<s:push>标签是把指定值暂时压入到值栈中,当执行完<s:push>标签后,压入到值栈中的值会被弹出。
<s:push value=”’hello’”/>等于把hello字符串压入到值栈中后,马上又弹出了,相当于什么都没做。
<s:push value=”#session.user”>把user压入到值栈中
<s:property value=”username”/>打印值栈元素的username属性
<s:property value=”password”/>打印值栈元素的password属性
</s:push>把user从值栈中弹出
<s:url>
<s:url>标签通常用来生成action路径,它与<c:url>标签很相似。
<s:url action=”TestAction”/>在页面中打印/contextpath/TestAction.action。也就是说它与<c:url>一样会生成全路径。而且无论给出后缀“.action”!action属性的值只需要与struts.xml文件中元素的name属性值相同即可。
<s:url action=”TestAction” namspace=”/” />还可以指定名称空间
<s:url action=”TestAction”>
<s:param name=”name” value=”’张三’”/>
</s:url>
页面中打印为:/ognl/TestAction.action?name=%E5%BC%A0%E4%B8%89
还可以为URL指定参数,其中参数包含中文时自动使用URL编码。
其中<s:param>是用来作为子标签的,它的作用是指定参数。它的value属性值为Ognl表达式,所以我们需要把“张三”用单引号引起来,表示Ognl表达式的字符串常量。
名称 必需 默认值 类型 说明
action 否 无 String 指定用于生成URL的action。
value 否 无 String 指定用于生成URL的地址值。通常只使用action或value其中一个属性。
var 否 无 String 如果指定了该属性,那么生成的URL不会输出到页面中,而是被保存到OgnlContext中。
method 否 无 String 指定调用action中的方法。如果使用的是value属性,那么该属性无效。
namespace 否 无 String 指定action所属的名称空间。
forceAddSchemeHostAndPort 否 false Boolean 当该属性为true时,生成的URL为绝对路径,而且会包含主机名及端口号。
<s:a>
它用来生成超链接,与<s:url>相似!
<s:a action=”TestAction” namespace=”/”>添加用户
<s:param name=”name” value=”’张三’”/>
</s:a>
<s:debug>
Debug标签用于调试,它在页面中生成一个“[Debug]”超链接,单击这个超链接,可以查看ValueStack和ActionContext中保存的所有对象。
2 Struts2通用标签之控制标签
控制标签很好理解,就是流程控制了。例如if、elseif等,以及iterator等。
<s:if>、<s:elseif>、<s:else>
这种标签大家应该一看就会用了。我们直接给出个小例子看看。
<!—
在浏览器中输入:http://localhost:8080/tagtest/index.jsp?score=85
–>
<s:set name="score" value="#parameters.score[0]"/>
<s:property value="#score"/>:
<s:if test="#score > 100 || #score < 0">
<s:property value="'输入错误'"/>
</s:if>
<s:elseif test="#score >= 90">
<s:property value="'A'" />
</s:elseif>
<s:elseif test="#score >= 80">
<s:property value="'B'" />
</s:elseif>
<s:elseif test="#score >= 70">
<s:property value="'C'" />
</s:elseif>
<s:elseif test="#score >= 60">
<s:property value="'D'" />
</s:elseif>
<s:else>
<s:property value="'E'"/>
</s:else>
<s:iterator>
<s:iterator>标签可以用来迭代一个集合,可以迭代的集合有:Collection、Map、Enumeration、Iterator或者是数组。iterator标签在迭代过程中,会把当前对象暂时压入值栈,这样在子标签中就可以直接访问当前对象的属性(因为当前对象在栈顶),在标签体执行完毕后,位于栈顶的对象就会被删除,在循环的第二圈时,把新的当前对象再压入值栈中。
<s:iterator value="{'zhangSan','liSi','wangWu' }">
name: <s:property/><br/>
</s:iterator>
如果为<s:iterator>标签指定了var属性,那么当前对象不只是压入到了值栈中,而且还会被添加到OgnlContext中。
<s:iterator value="{'zhangSan','liSi','wangWu' }" var="name">
name: <s:property value="#name"/><br/>
</s:iterator>
名称 必需 默认值 类型 说明
var 否 无 String 如果指定了该属性,那么迭代的集合中的元素将被保存到OgnlContext中,可以通过该属性的值来引用集合中的元素。该属性几乎不被使用。
value 否 无 Coolection、Map、Enumeration、Iterator 或数组 指定迭代的集合。如果没有指定该属性,那么iterator标签将把位于值栈栈顶的对象放入一个新创建的List中进行迭代。
status 否 无 String 如果指定了该属性,一个IteratorStatus实例将被放入到OgnlContext中,通过该实例可以获取迭代过程中的一些状态信息。
IteratorStatus类在org.apahce.struts2.views.jsp包下。下面是对该类常用方法的介绍:
public int getCount():得到当前已经迭代的元素的总数。
public int getIndex():得到当前迭代的元素的索引。
public boolean isEven():判断当前迭代的元素的个数是否是偶数。
public boolean isOdd():判断当前迭代的元素的个数是否是奇数。
public boolean isFirst():判断当前迭代的元素是否是第一个元素。
public boolean isLast():判断当前迭代的元素是否是最后一个元素。
在OGNL表达式中使用IteratorStatus类的方法时,可以直接使用:count、index、even、odd、first、last属性。
<s:iterator value='{"one", "two", "three"}' status="status">
<s:property value="#status.count"/>,
<s:property value="#status.index"/>,
<s:property value="#status.even"/>,
<s:property value="#status.odd"/>,
<s:property value="#status.first"/>,
<s:property value="#status.last"/><br/>
</s:iterator>
<hr/>
<s:iterator value="#{'1':'one','2':'two','3':'three','4':'four'}" status="st">
<s:property value="key"/>:<s:property value="value"/><br/>
</s:iterator>
3 Struts2标签UI标签之表单标签
Struts2的表单标签还是比较好用的,但它也存在一些败笔,例如主题这一部分就不是很灵活。所以导致开发中没有公司会使用它提供的主题。
Struts2标签的优势:
简化代码;
自动数据回显;
指定主题样式(说是优点,但很多人也会认为这是缺点);
- 表单标签入门
我们首先使用一个登录表单来对比一下html表单和Struts2表单的区别。
<form action="<c:url value='/user/LoginAction.action'/>" method="post">
用户名 <input type="text" name="username"/><br/>
密 码 <input type="password" name="password"/><br/>
<input type="submit" value="登录"/>
</form>
<hr/>
<s:form action="LoginAction" namespace="/user">
<s:textfield name="username" label="用户名" />
<s:password name="password" label="密 码" />
<s:submit value="登录" />
</s:form>
<form action="/ognl/user/LoginAction.action" method="post">
用户名 <input type="text" name="username"/><br/>
密 码 <input type="password" name="password"/><br/>
<input type="submit" value="登录"/>
</form>
<hr/>
<form id="LoginAction" name="LoginAction" action="/ognl/user/LoginAction.action" method="post">
<table class="wwFormTable">
<tr>
<td class="tdLabel"><label for="LoginAction_username" class="label">用户名:</label></td>
<td
><input type="text" name="username" value="" id="LoginAction_username"/></td>
</tr>
<tr>
<td class="tdLabel"><label for="LoginAction_password" class="label">密 码:</label></td>
<td
><input type="password" name="password" id="LoginAction_password"/></td>
</tr>
<tr>
<td colspan="2"><div align="right"><input type="submit" id="LoginAction_0" value="登录"/>
</div></td>
</tr>
</table></form>
通过上面生成的代码可以看来:
<s:form>
通过action和namespace两部分来指定请求路径,action直接给元素的name值即可,无需给出后缀“.action”;
method默认为post;
会自动添加id属性,值与action属性值相同;
整个表单会生成在
<s:textfield>
对应标签;
通过lable来生成标签;
<s:password>
对应标签;
通过label来生成标签
<s:submit>
对应标签;
- 表单主题
我们发现,整个表单都会在中生成,这也就说明无需为每个表单项添加
。因为在表格中就无需再换行了。
生成在表格中是因为<s:form>标签的theme属性的默认值为xhtml,它表示一个主题样式。这个主题样式由Freemarker模板来完成。如果你不希望使用这个xhtml主题,那么有下列3种方法来修改主题:
在<s:textfield>的theme属性指定为simple,那么这个表单项就使用简单主题;
在<s:form>的theme属性指定为simple,那么整个表单都使用简单主题;
设置struts.ui.theme常量为simple,那么所有表单标签的默认主题都是simple。
当表单设置为theme=”simple”之后:
<s:form action=“LoginAction” namespace=“/user” theme=“simple”>
<s:textfield name=“username” label=“用户名” />
<s:password name=“password” label=“密 码” />
<s:submit value=“登录” />
</s:form>
这时没有表格来格式化表单,而且也不能生成,这说明样式都要自来来设定。好处是你重新获得了自由,坏处是一切都要靠自己了。
- 自动回显
我们知道,当表单提交后,再返回到表单页面后,html标签不可能帮我们回显数据。而Struts2的表单标签可以做到这一点。原因是当前Action就在值栈顶,而表单标签会从值栈中获取数据来回显。
4 表单标签之选择性标签
- <s:redio>标签
表单标签中简化比较大的标签可以不在需要写循环,还有就是自动回显。我们都知道在下拉列表、单选、复选中,手动回显是比较麻烦的事。但使用Struts2的表单标签就方便多了。
<s:radio list=“#{‘male’:‘男’,‘female’:‘女’}” name=“gender”/>
list指定的是一个Map,默认key为实际值,而value为显示值。
也可以为list属性指定为list:
<s:radio list=“{‘男’,‘女’}” name=“gender”/>
这时实际值和显示值是相同的。
- <s:checkboxlist>标签
这个东西也很好用,一下子可以显示很多复选按钮。而且自动回显!
<s:checkboxlist list=“#{‘read’:‘看书’,‘netplay’:‘上网’,‘music’:‘音乐’ }” name=“hobby”/>
- <s:select>标签
下拉列表与上面两个标签也一样,都是选择性的!使用它也是无需循环,无需处理循环。
<s:select name=“city” list=“#{‘bj’:‘北京’,‘sh’:‘上海’,‘gz’:‘广州’}”/>
国际化
1 什么是国际化
一款软件可以为不同国家的来访者提供不同语言的界面,那么这个软件就是国际化的。这需要为每种上语言提供资源包(很多语言包),程序通过来访者的国家和语言来定位资源包。
基本名称_语言_国家.properties
例如:res_zh_CN.properties、res_en_US.properties
2 Struts2国际化文件分类
全局国际化文件:整个程序都可以使用(最常用)
特定包中可以使用(一个包中所有Action可以使用);
特定Action中可以使用(一个Action可以使用);
临时信息文件(JSP中i18n标签中可以使用)
3 国际化信息的应用场景
JSP页面中使用国际化信息;
Action中使用国际化信息;
配置文件中使用国际化信息。
4 全局国际化
res_zh_CN.properties
#用户名
username=\u7528\u6237\u540D
#用户名不能为空
error.username=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
- 指定资源文件路径:
在struts.xml文件中指定资源文件的路径:
这说明在src目录下需要有res_语言_国家.properties文件。
- 在JSP中使用<s:text>标签获取资源文件信息
在JSP中使用
<s:fielderror />
<form action="<c:url value='Demo1Action.action'/>" method="post">
<s:text name="username"/>:<input type="text" name="username"/><br/>
<input type="submit" value="提交"/>
</form>
在Action中使用
public class Demo1Action extends ActionSupport {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String execute() throws Exception {
System.out.println(this.getText("error.username"));
return NONE;
}
}
在配置文件中使用,例如在校验文件中使用。
<validators>
<field name="username">
<field-validator type="requiredstring">
<message key="error.username" />
</field-validator>
</field>
</validators>
5 局部国际化文件
全局国际化文件是整个程序中都可以使用的,文件的位置需要在struts.xml文件中指定。而局部国际化文件不需要在struts.xml文件中指定,但文件的名称,以及位置是固定的。
局部国际化文件分为两种:特定Action中可以使用,以及特定包及子包中可以使用。
Action国际化文件
例如想提供一个只能在cn.itcast.action.TestAction中使用的国际化文件,那么就需要在cn.itcast.action包下创建TestAction_zh_CN.properties文件,这个文件的基本名称必须与Action的名称相同。
包国际化文件
例如想提供一个只能在cn.itcast.action包中使用的国际化文件,那么就需要在cn.itcast.action包下创建package_zh_CN.properties文件。这个文件也可以在cn.itcast.action包的子包中被使用。例如有Actoin的命名为:cn.itcast.action.demo1.Demo1Action,在这个Action中也可以cn.itcast.action.package_zh_CN.properties文件。
6 临时国际化文件
临时国际化文件是在<s:i18n>标签中使用。
<s:i18n name=“cn.itcast.demo1.action.res”>
<s:text name=“username” />
</s:i18n>
<s:i18n>标签的name属性指定国际化文件的位置,这说明需要在src/cn.itcast.demo1/action/目录下需要有res_zh_CN.properties文件。
7 在国际化文件中使用占位符
在国际化文件中使用占位符,例如在国际化文件中有如下内容:
login.error={0}或{1}不能为空
其中{0}和{1}就是点位符,这需要指定值来替换占位符。
String s = this.getText(“login.error”, new String[]{“用户名”, “密码”});
System.out.println(s);//用户名或密码不能为空。
上传
1 上传下载组件介绍
jspSmartUpload(model1的年代);
apache-commons-fileupload,Struts2默认上传组件;
Servlet3.0使用的Part,但Servlet3.0还没有普及;
COS,Struts2支持,不过已经停止更新很久了;
pell,Struts2支持。
2 fileUpload的拦截器
Struts2默认使用的是commons-fileUpload组件完成上传的,使用Struts2会大量简化上传文件的开发。这一工作由fileUpload拦截器来完成。它会查看当前请求的enctype是否为multipart/form-data,如果不是就会直接“放行”;如果是,那么它会去解析表单,然后把解析的结果传递给Action的属性!
fileUpload拦截器对会对Action提供很大的“帮助”,同时它也会对Action提出一些“小小的要求”。Action需要提供3个属性:
File fieldName
String fieldNameContentType
String fieldNameFileName;
三个属性的前缀都(fieldName)必须与文件表单项名称一致,例如有文件表单项内容为:,其中表单项名称为:myUpload,那么Action就必须要有如下3个属性:
private File myUpload
private String myUploadContentType
private String myUploadFileName
3 演示上传
上传文件首先我们需要给出一个页面,页面中要有一个表单:
upload.jsp
<form action="<c:url value='/UploadAction.action'/>" method="post" enctype="multipart/form-data">
用户名: <input type="text" name="username"/><br/>
文 件:<input type="file" name="myUpload"/><br/>
<input type="submit" value="Submit"/>
</form>
UploadAction
public class UploadAction extends ActionSupport {
private String username;
public void setUsername(String username) {
this.username = username;
}
private File myUpload;
private String myUploadContentType;
private String myUploadFileName;
private String savepath = "/WEB-INF/uploads";
public void setMyUpload(File myUpload) {
this.myUpload = myUpload;
}
public void setMyUploadContentType(String myUploadContentType) {
this.myUploadContentType = myUploadContentType;
}
public void setMyUploadFileName(String myUploadFileName) {
this.myUploadFileName = myUploadFileName;
}
public String execute() throws Exception {
System.out.println(username);
System.out.println(this.myUploadContentType);
System.out.println(this.myUploadFileName);
System.out.println(this.myUpload.getAbsolutePath());
this.savepath = ServletActionContext.getServletContext().getRealPath(savepath);
File destFile = new File(savepath, myUploadFileName);
FileUtils.copyFile(myUpload, destFile);
return NONE;
}
}
struts.xml
<package name="s8" namespace="/" extends="struts-default">
<action name="UploadAction" class="cn.itcast.upload.action.UploadAction">
</action>
</package>
4 上传配置
可以通过Struts2的常量来完成对上传的配置,下面是与上传相关的常量:
struts.multipart.parser:指定使用的上传组件,默认值为jakarta,表示使用commons-fileupload组件,Struts2还支持cos和pell;
struts.multipart.saveDir:临时目录,如果没有指定临时目录,那么临时文件会在Tomcat的work目录中;
struts.multipart.maxSize:整个大小限制,默认值为2097152,即2M。注意,这个限制是整个请求的大小,而不是单一文件的大小。
当上传的表单超出了限制时,拦截器会向fieldError中添加错误信息!当执行wokflow拦截器时,会发现fieldError中存在错误,这时就会跳转到input视图,所以我们需要为Action指定input视图。
fileUpload拦截器也有3个参数,我们可以给fileUpload拦截器配置这3个参数:
maximumSize:上传的单个文件的大小限制;
allowedTypes:允许上传文件的类型,多个类型以逗号隔开;
allowedExtensions:允许上传文件的扩展名,多个扩展名以逗号隔开;
<struts>
<constant name="struts.devMode" value="true" />
<constant name="struts.multipart.maxSize" value="1048576" />
<package name="s8" namespace="/" extends="struts-default">
<action name="UploadAction" class="cn.itcast.upload.action.UploadAction">
<result name="input">/demo1/upload.jsp</result>
<param name="savepath">/WEB-INF/uploads</param>
<interceptor-ref name="defaultStack">
<!-- 限制单个文件大小上限为512K -->
<param name="fileUpload.maximumSize">524288</param>
<param name="fileUpload.allowedExtensions">jpg,png,bmp</param>
</interceptor-ref>
</action>
</package>
</struts>
5 上传相关的错误信息国际化
在上传文件时如果出现错误,那么在input视图显示的错误信息都是英文的。如果想替换这些信息,需要知道这些错误信息的资源key,然后在我们自己的国际化资源文件中指定这些key的新值即可。
与上传相关的错误信息都在org.apache.struts2包下的struts-message.properties文件中。
struts.messages.error.uploading=Error uploading: {0}
struts.messages.error.file.too.large=The file is to large to be uploaded: {0} “{1}” “{2}” {3}
struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} “{1}” “{2}” {3}
struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} “{1}” “{2}” {3}
struts.messages.upload.error.SizeLimitExceededException=Request exceeded allowed size limit! Max size allowed is: {0} but request was: {1}!
我们可以在src下res.properties文件,在这个文件中对象以上资源key进行替换。然后在struts.xml文件中给出即可。
6 多文件上传
当需要上传多个文件时,如果每个对应Action的3个属性,那么这会使Action的属性过多的现象。处理这一问题的方法是在表单中设置所有的名称为相同名称,然后在Action中给出数组属性即可。
uploads.jsp
<s:fielderror />
<s:actionerror/>
<form action="<c:url value='/UploadsAction.action'/>" method="post" enctype="multipart/form-data">
用户名: <input type="text" name="username"/><br/>
文 件1:<input type="file" name="myUpload"/><br/>
文 件2:<input type="file" name="myUpload"/><br/>
文 件3:<input type="file" name="myUpload"/><br/>
<input type="submit" value="Submit"/>
</form>
UploadsAction
public class UploadsAction extends ActionSupport {
private File[] myUpload;
private String[] myUploadContentType;
private String[] myUploadFileName;
private String savepath;
public void setSavepath(String savepath) {
this.savepath = savepath;
}
public void setMyUpload(File[] myUpload) {
this.myUpload = myUpload;
}
public void setMyUploadContentType(String[] myUploadContentType) {
this.myUploadContentType = myUploadContentType;
}
public void setMyUploadFileName(String[] myUploadFileName) {
this.myUploadFileName = myUploadFileName;
}
public String execute() throws Exception {
savepath = ServletActionContext.getServletContext().getRealPath(savepath);
for(int i = 0; i < myUpload.length; i++) {
System.out.println("文件名:" + myUploadFileName[i]);
System.out.println("文件类型:" + myUploadContentType[i]);
FileUtils.copyFile(myUpload[i], new File(savepath, myUploadFileName[i]));
}
return NONE;
}
}
其中Struts2也允许我们使用List类型来代替数组属性。
UploadsAction.java
public class UploadsAction extends ActionSupport {
private List<File> myUpload;
private List<String> myUploadContentType;
private List<String> myUploadFileName;
…
}