《研磨struts2》第五章 Result 之 5.2 预定义的Result
5.2 预定义的Result
5.2.1 预定义的ResultType
在Struts2中,预定义了很多ResultType,其实就是定义了很多展示结果的技术。Struts2把内置的<result-type>都放在struts-default包中。struts-default包就是我们配置的包的父包,在第4章已经看到这个包定义在struts2-core-2.1.8.1.jar包中的根目录下的文件struts-default.xml中。
在这个包中,可以找到相关的<result-type>的定义,<result-types>元素是<package>元素的直接子元素。Struts2预定义如下:
<result-types> <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/> <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/> <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/> <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/> <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/> <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/> <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/> <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/> <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/> <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" /> </result-types>
上面的每一个<result-type>元素都是一种视图技术或跳转方式的封装。其中的name属性是在<result>元素中如何引用这种视图技术或跳转方式,对应着<result>元素的type属性的值。
可能有朋友会说,我们在配置<result>元素的时候,没有配置过type属性嘛。没错,你确实没有配置过,原因就在于Struts2里面设置了默认的type,就是上面加粗的那句话,你没有配置,默认就是“dispatcher”。这个“dispatcher”的技术就相当于在Servlet里面的“RequestDispatcher”的技术,也就是一个页面跳转的技术。
而class属性是这种视图技术或跳转方式的具体实现类,这些实现类都已经是Struts2实现好的,我们只需要引用就可以了。
5.2.2 如何配置使用
Result的配置非常简单,<result>元素可以有name属性和type属性,但是两种属性都不是必须的。
1:配置name属性
按照前面的讲述,name属性是用来跟Action的execute方法返回的字符串相对应的,用来指示Action运行后跳转到的下一个页面,因此name属性的值可以是任意字符串。比如有如下的execute方法:
public String execute() throws Exception { return "toWelcome"; }
那么,这里返回的“toWelcome”,在struts.xml里面就有如下的配置来对应:
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction"> <result name="toWelcome">/s2impl/welcome.jsp</result> </action>
如果不设置的话,默认值为“success”,正好和Action中的“SUCCESS”这个常量相对应,那样的话,execute方法就应该返回SUCCESS,如下:
public String execute() throws Exception { return this.SUCCESS; }
此时在struts.xml里面就有如下的配置来对应:
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction"> <result>/s2impl/welcome.jsp</result> </action>
或者是:
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction"> <result name="success">/s2impl/welcome.jsp</result> </action>
2:配置type属性
<result>元素的type属性也可以是任意字符串,不过,一定是某一个<result-type>元素的name属性。在没有自定义ResultType的情况下,type属性的值,就是在struts-default.xml中所定义的<result-type>的name属性的值。比如:
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction"> <result name="toWelcome" type="dispatcher">/s2impl/welcome.jsp</result> </action>
这里的“dispatcher”就是在struts-default.xml中所定义的默认的<result-type>的name属性值。既然是默认的,那就可以不用配置,也就是说,上面的配置跟如下配置是等价的,示例如下:
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction"> <result name="toWelcome">/s2impl/welcome.jsp</result> </action>
好了,基本的Result配置使用就差不多了,接下来,挑选几个常见的ResultType,来看看如何配置并使用它们。
5.2.3 名称为dispatcher的ResultType
1:基本使用
名称为“dispatcher”的ResultType,在struts-default.xml里的配置如下:
<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
通过配置可以看出,它对应的实现类是ServletDispatcherResult。
如果采用JSP作为视图的实现技术,那么这个ResultType是最常用的。在这个ResultType的实现中,调用了javax.servlet.RequestDispatcher类的forward方法,也就是说它相当于是对RequestDispatcher的一个再包装。
既然是这样,那么在Servlet中使用RequestDispatcher来进行页面跳转的特性,也就自然被“dispatcher”这个ResultType继承下来了。那么Servlet中的RequestDispatcher,到底有什么特性呢?
那就是通过RequestDispatcher来进行页面跳转,将会保持是同一个请求对象。这有什么好处呢?由于是同一个对象,那就意味着有同样的数据,而请求对象里面数据众多,在Servlet的request对象里面,典型有如下数据:
- 参数区(parameter),就是用户在页面上填写并提交的数据
- Head区,由浏览器在发出请求的时候,自动加入到请求包的数据
- 属性区(Attribute),由开发人员存储在属性区的值,通常是通过request.setAttribute方法、request.getAttribute方法来进行访问
- Cookie区,由浏览器在发出请求的时候,自动把相关的Cookie数据通过request传递到服务端
好处是不是很大?因此这也是使用Struts2来进行web开发中最常使用的ResultType。至于怎么使用,前面很多例子都是用的“dispatcher”,这里就不再赘述了。
2:几个小知识点
(1)在<result>元素的定义中可以使用Action的execute方法运行之后的数据。怎么做呢?一起来看看示例。
或许我们都已经习惯于以下这种简单的<result>配置:
<result name="toWelcome">/s2impl/welcome.jsp</result>
里面用于指定jsp位置的字符串都是固定的。如果我们希望这个字符串是活动的,可以根据某些参数值来变化,该怎么做到呢?
如果我们在Action中定义一个folder字符串,并在execute中对它赋值:
public class HelloWorldAction extends ActionSupport { private String account; private String password; private String submitFlag; private String folder; public void setFolder(String folder){ this.folder = folder; } public String getFolder(){ return folder; } public String execute() throws Exception { this.folder = "s2impl"; return "toWelcome"; } //属性对应的getter/setter方法,省略了 }
那么,在<result>的定义中就可以引用folder这个变量,示例如下:
<result name="toWelcome">/${folder}/welcome.jsp</result>
这样配置的结果和前面写死的路径效果时完全一样的。
仔细观察一下你会发现,“${folder}”的写法跟以前在jsp上写的el表达式类似,而里面的“folder”是跟Action的属性相对应的。
(2)对于dispatcher的使用范围,除了可以配置jsp外,还可以配置其他的web资源,比如其他的Servlet等。
如果在web.xml中有如下配置:
<servlet> <servlet-name>login</servlet-name> <servlet-class>cn.javass.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
那么在struts.xml中可以如下配置:
<result name="toWelcome">/login</result>
但是请注意,如果这个web资源是另一个Action的话,不能这么配置,需要使用Struts2的另一种名称为“chain”的ResultType。
(3)使用“dispatcher”的ResultType,不能访问其他web应用中的web资源。当然,这个特性是由javax.servlet.RequestDispatcher类的forward方法决定的。
(4)更完整的配置方式
平时把result对应的jsp的路径,直接作为<result>元素中的文本来配置,这是简化的写法,实际上对于dispatcher还有两个参数可以配置,示例如下:
<result name="toWelcome" type="dispatcher"> <param name="location">/s2impl/welcome.jsp</param> <param name="parse">true</param> </result>
location参数就是咱们平时写的下一个jsp的位置,而parse参数决定了location是否可以通过使用OGNL来引用参数,默认为true。
其实,前面使用${folder}来引用Action的folder属性的值的例子,就是使用的OGNL来引用参数。
5.2.4 名称为redirect的ResultType
1:基本使用
名称为“redirect”的ResultType,在struts-default.xml里的配置如下:
<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
通过配置可以看出,它对应的实现类是ServletRedirectResult。
这种Result同常也使用JSP作为视图技术。它包装的是javax.servlet.http.HttpServletResponse类的sendRedirect方法,这个ResultType也是用来实现跳转到下一个页面的。
但是它的功能与上面的dispatcher不同,“redirect”的特点是全新的请求,这就意味着,本次请求和跳转到下一个页面的请求是不同的对象,因此它们的值是不一样的。
可以通过如下的方式来测试一下。修改前面dispatcher的示例,主要是修改struts.xml,需要把result的type设置为“redirect”。示例如下:
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction"> <result name="toWelcome" type="redirect">/s2impl/welcome.jsp</result> </action>
其他的可以不用改变,然后回到登录页面,填写帐号和密码,然后点击登录按钮,正确的跳转到了欢迎页面,如下图所示:
图5.1 使用redirect的欢迎页面
仔细看看,有什么不同?
很明显,这里没有正确的显示出帐号的值来,看看后台有值吗?肯定是有的。这就说明经过“redirect”这个ResultType,跳转到欢迎页面的时候,两个请求对象不是一个了,因此在欢迎页面无法获取到帐号的值了。
你可以把struts.xml中,type=“redirect”的配置去掉,或者改成type=“dispatcher”,再次运行看看,欢迎页面就应该有值了。
2:几个小知识点
对比着dispatcher的ResultType,来看看相应的几个问题。
(1)同样在<result>元素的定义中可以使用Action的execute方法运行之后的数据。
测试示例,同样在Action中定义一个folder字符串,并在execute中对它赋值,跟前面一样,这里就不去赘述了。
那么,在<result>的定义中就可以引用folder这个变量,示例如下:
<result name="toWelcome" type="redirect">/${folder}/welcome.jsp</result>
由于redirect采取重定向的方式,下一个页面会取不到上一个请求对象里面的值,如果要传值的话,可以采用get的方式传参。示例如下:
<result name="toWelcome" type="redirect">/${folder}/welcome.jsp?account=${account}</result>
上面这个配置,会向新请求里面传入account的参数,这样在欢迎页面就可以获取到account的值了。
但是,前面写的欢迎页面是取不到这个account的值的,为什么呢?先来看看前面写的欢迎页面取值的那句话,如下:
欢迎账号为<s:property value="account"/>的朋友来访
以前的欢迎页面,是通过使用Struts2的标签来获取的account的值,Struts2的标签会到Struts2的值栈里面去取值,而这里是执行Result的时候,才再请求上添加了account这么一个参数,然后就直接回到页面了,根本不会再走一次Struts2的运行过程,也就是说,这里传递的这个参数,根本不会进入到这个请求对应的值栈,因此这里这个写法是取不到值的。
那么该怎么写才能获取到这个account参数的值呢?
有两个简单的方法,一个是直接使用Servlet的HttpServletRequest对象的方法来获取参数,另外一个方法是直接使用EL表达式,都是可以的,示例如下:
欢迎账号为<%=request.getParameter("account") %>的朋友来访 欢迎账号为${param['account']}的朋友来访
再次测试看看,此时的欢迎页面就应该有account的值了。
(2)对于redirect的使用范围,除了可以配置jsp外,还可以配置其他的web资源,比如其他的Servlet等。具体写法跟dispatcher一样,不再赘述。
(3)使用“redirect”的ResultType,可以访问其他web应用中的web资源,甚至是任何你想要访问的网站都可以。
(4)更完整的配置方式
与“dispatcher”一样,“redirect”也可以配置<param>,同样可以配置location和parse,连含义都是一样的,因此就不去示例了。
5.2.5 名称为chain的ResultType、
1:基本使用
名称为“chain”的ResultType,在struts-default.xml里的配置如下:
<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
chain是一种特殊的视图结果,用来将Action执行完之后链接到另一个Action中继续执行,新的Action使用上一个Action的上下文(ActionContext),数据也会被传递。
这在实际开发中,也是经常用到的一种ResultType。比如我们在Servlet开发中,一个请求,被一个Servlet处理过后,不是直接产生相应,而是把这个请求传递到下一个Servlet继续处理,直到需要的多个Servlet处理完成后,才生成响应返回。
同样的,在Struts2开发中,也会产生这样的需要,一个请求被一个Action处理过后,不是立即产生响应,而是传递到下一个Action中继续处理。那么这个时候,就需要使用chain这个ResultType了。
来示例一下,先看看第一个Action,就用HelloWorldAction吧,稍微简化一下,示例如下:
public class HelloWorldAction extends ActionSupport { private String account; private String password; private String submitFlag; public String execute() throws Exception { this.businessExecute(); return "toSecond"; } public void businessExecute(){ System.out.println("用户输入的参数为==="+"account="+account+",password="+password+",submitFlag="+submitFlag); } //属性对应的getter/setter方法,省略了 }
第二个Action,示例代码如下:
public class SecondAction extends ActionSupport { public String execute() throws Exception { System.out.println("现在SecondAction进行处理"); return "toWelcome"; } }
然后到struts.xml中,配置这两个Action,要注意第一个Action的配置,在配置“toSecond”这个result的时候,用的就是chain这个ResultType,示例如下:
<package name="helloworld" extends="struts-default"> <action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction"> <result name="toSecond" type="chain"> <param name="actionName">secondAction</param> </result> </action> <action name="secondAction" class="cn.javass.action.action.SecondAction"> <result name="toWelcome">/s2impl/welcome.jsp</result> </action> </package>
其他的可以不用改变,然后回到登录页面,填写帐号和密码,然后点击登录按钮,正确的跳转到了欢迎页面,如下图所示:
图5.2 使用chain的欢迎页面
仔细观察上面的欢迎页面,会发现能够访问到用户在登录页面填写的帐号数据。再来看看后台,会输出如下信息:
用户输入的参数为===account=test,password=111111,submitFlag=login 现在SecondAction进行处理
其中第一行是HelloWorldAction输出的信息,而第二行,很明显是SecondAction输出的信息了。也就是说,用户提交登陆请求,只发出了一次请求,但是有两个Action来处理了这个请求。这个请求先被HelloWorldAction进行处理,然后链接到SecondAction,由SecondAction来继续处理这个请求,并在处理完成后,产生响应,回到欢迎页面。
2:几个小知识点
(1)chain不能在result配置的时候传递参数,也就是说,不能类似于如下的配置:
<result name="toSecond" type="chain"> <param name="actionName">secondAction?account=5</param> </result>
这种配置方式是不行的,因为这里要求配置的是要链接的Action的name,不能传递参数,那么,要传递参数怎么办呢?
那就需要在Action里面使用ActionContext或者ServletActionContext,由于还没有学到,这里先不去示例了。
(2)在上面示例的欢迎页面,照样输出了用户在登录页面提交给HelloWorldAction的值,这说明使用chian的方式,后面的Action会和前面的Action共用同一个ActionContext,简单点说就是有同样的数据。
(3)名称为“chain”的ResultType在配置的时候,除了前面示例中的actionName外,还有一个参数,名称为“namespace”,表示被链接的Action所在包的命名空间,如果不设置,默认的即是当前的命名空间。配置示例如下:
<result name="toSecond" type="chain"> <param name="actionName">secondAction</param> <param name="namespace">其他Package的namespace</param> </result>
5.2.6 名称为freemarker的ResultType
Struts2除了支持以JSP作为视图技术之外,还支持其他的模板技术,比如FreeMarker和Velocity。这里仅以FreeMarker为例,来讲述一下Struts2如何使用其他模板技术。
FreeMarker是一个纯Java模板引擎,是一种基于模板来生成文本的工具。具体的FreeMarker的知识这里就不去介绍了,感兴趣的朋友可以到http://freemarker.sourceforge.net/去获取详细的信息。
freemarker的ResultType,顾名思义,就是用来处理结果页面为使用FreeMarker制作的页面的这样一种ResultType。在struts-default.xml中,名称为“freemarker”的ResultType的配置如下:
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
下面通过一个简单的示例,来看看如何使用名称为freemarker的ResultType。
1:沿用前面的资源
HelloWorldAction和login.jsp都和前面的示例一样,这里就不再赘述了。
2:制作Freemarker的页面welcome.ftl
这个页面很简单,只是简单的输出帐号信息,示例代码如下:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> <title>Insert title here</title> </head> <body> 欢迎账号为${account}的朋友来访 </body> </html>
这个页面就像一个普通的jsp,其中的${account},就像在jsp中调用el表达式一样。而其中的内容,就像以前在welcome.jsp中使用el表达式${ account }取值一样。
这里要注意的一个问题是,究竟这个welcome.ftl放在什么地方?
FreeMarker对应的ftl文件,并不是放在WebContent下,而是放在classes的路径下,在Eclipse中开发的时候,放到src下,因为Eclipse会自动将其编译到classes路径去。
3:配置struts.xml
主要是要修改toWelcome的result配置,示例代码如下:
<action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction"> <result name="toWelcome" type="freemarker">welcome.ftl</result> </action>
注意其中的type是freemarker,对应的值是“welcome.ftl”,意味着在classes的根下有一个“welcome.ftl”的文件。如果对应的值是“/aa/ welcome.ftl”,那就表明在classes的根下有一个名称为“aa”的文件夹,在aa文件夹下有一个名为“welcome.ftl”的文件
4:关于freemarker的配置
freemarker类型Result的共有4个参数,示例如下:
<result name="toWelcome" type="freemarker"> <param name="location">welcome.ftl</param> <param name="parse">true</param> <param name="contentType">text/html</param> <param name="writeIfCompleted">false</param> </result>
- location用于指定ftl文件的位置。
- parse默认为true,指定在location中出现的形如${name}的表达式是否会被解析。
- contentType默认为text/html,指定输出方式。
- writeIfCompleted默认为false,指定是否不存在ftl解析错误的时候才写入到流中。
使用freemarker类型Result之后,可以发现,在Struts2中,同一个web应用可以同时使用多种不同的视图技术。
如果一个web应用中,以freemarker类型的Result为主的话,完全可以设置freemarker类型的Result为默认的Result。 只需要在包中覆盖对freemarker类型的声明,设置default属性为true即可,示例如下:
<package name="hello" extends="struts-default"> <result-types> <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult" default="true"/> </result-types> <action ……> <result name="toWelcome" type="freemarker"> welcome.ftl </result> </action> </package>
5.2.7 其他ResultType
除了前面提到的这些result,Struts2还提供其他的Result,比如用于同Velocity、xslt等的结合,这里简单的介绍一下:
1:velocity:用来处理velocity模板。Velocity是一个模板引擎,可以将Velocity模板转化成数据流的形式,直接通过JavaServlet输出。
2:xslt:用来处理处理XML/XLST模板,将结果转换为xml输出。
3:httpheader:用来控制特殊HTTP行为
4:stream:用来向浏览器进行流式输出
私塾在线网站原创《研磨struts2》系列
转自请注明出处:【http://sishuok.com/forum/blogPost/list/0/4051.html】
欢迎访问http://sishuok.com获取更多内容