《研磨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预定义如下:

 

java代码:
  1. <result-types>  
  2. <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>  
  3. <result-type name="dispatcher"  
  4. class="org.apache.struts2.dispatcher.ServletDispatcherResult"  
  5. default="true"/>  
  6. <result-type name="freemarker"   
  7.     class="org.apache.struts2.views.freemarker.FreemarkerResult"/>  
  8. <result-type name="httpheader"   
  9.     class="org.apache.struts2.dispatcher.HttpHeaderResult"/>  
  10. <result-type name="redirect"   
  11.     class="org.apache.struts2.dispatcher.ServletRedirectResult"/>  
  12. <result-type name="redirectAction"   
  13.     class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>  
  14. <result-type name="stream"  
  15. class="org.apache.struts2.dispatcher.StreamResult"/>  
  16. <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>  
  17. <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>  
  18. <result-type name="plainText"  
  19. class="org.apache.struts2.dispatcher.PlainTextResult" />  
  20. </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方法:

 

java代码:
  1. public String execute() throws Exception {  
  2.         return "toWelcome";  
  3. }  

那么,这里返回的“toWelcome”,在struts.xml里面就有如下的配置来对应:

 

java代码:
  1. <action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">  
  2.             <result name="toWelcome">/s2impl/welcome.jsp</result>  
  3. </action>  

如果不设置的话,默认值为“success”,正好和Action中的“SUCCESS”这个常量相对应,那样的话,execute方法就应该返回SUCCESS,如下:

 

java代码:
  1. public String execute() throws Exception {  
  2.         return this.SUCCESS;  
  3. }  

此时在struts.xml里面就有如下的配置来对应:

 

java代码:
  1. <action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">  
  2.             <result>/s2impl/welcome.jsp</result>  
  3. </action>  

或者是:

 

java代码:
  1. <action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">  
  2.             <result name="success">/s2impl/welcome.jsp</result>  
  3. </action>  

2:配置type属性

<result>元素的type属性也可以是任意字符串,不过,一定是某一个<result-type>元素的name属性。在没有自定义ResultType的情况下,type属性的值,就是在struts-default.xml中所定义的<result-type>的name属性的值。比如:

 

java代码:
  1. <action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">  
  2.             <result name="toWelcome" type="dispatcher">/s2impl/welcome.jsp</result>  
  3. </action>  

这里的“dispatcher”就是在struts-default.xml中所定义的默认的<result-type>的name属性值。既然是默认的,那就可以不用配置,也就是说,上面的配置跟如下配置是等价的,示例如下:

 

java代码:
  1. <action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">  
  2.             <result name="toWelcome">/s2impl/welcome.jsp</result>  
  3. </action>  

好了,基本的Result配置使用就差不多了,接下来,挑选几个常见的ResultType,来看看如何配置并使用它们。

5.2.3  名称为dispatcherResultType

1:基本使用

名称为“dispatcher”的ResultType,在struts-default.xml里的配置如下:

 

java代码:
  1. <result-type name="dispatcher"  
  2. class="org.apache.struts2.dispatcher.ServletDispatcherResult"  
  3. 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>配置:

 

java代码:
  1. <result name="toWelcome">/s2impl/welcome.jsp</result>  

里面用于指定jsp位置的字符串都是固定的。如果我们希望这个字符串是活动的,可以根据某些参数值来变化,该怎么做到呢?

       如果我们在Action中定义一个folder字符串,并在execute中对它赋值:

 

java代码:
  1. public class HelloWorldAction extends ActionSupport {  
  2.     private String account;  
  3.     private String password;  
  4.     private String submitFlag;  
  5.     private String folder;  
  6.     public void setFolder(String folder){  
  7.         this.folder = folder;  
  8.     }  
  9.     public String getFolder(){  
  10.         return folder;  
  11.     }  
  12.   
  13.       
  14.     public String execute() throws Exception {  
  15.         this.folder = "s2impl";  
  16.         return "toWelcome";  
  17.     }  
  18.     //属性对应的getter/setter方法,省略了  
  19. }  

那么,在<result>的定义中就可以引用folder这个变量,示例如下:

 

java代码:
  1. <result name="toWelcome">/${folder}/welcome.jsp</result>  

这样配置的结果和前面写死的路径效果时完全一样的。

       仔细观察一下你会发现,“${folder}”的写法跟以前在jsp上写的el表达式类似,而里面的“folder”是跟Action的属性相对应的。

(2)对于dispatcher的使用范围,除了可以配置jsp外,还可以配置其他的web资源,比如其他的Servlet等。

       如果在web.xml中有如下配置:

 

java代码:
  1. <servlet>  
  2.     <servlet-name>login</servlet-name>  
  3.     <servlet-class>cn.javass.servlet.LoginServlet</servlet-class>  
  4. </servlet>  
  5. <servlet-mapping>  
  6.     <servlet-name>login</servlet-name>  
  7.     <url-pattern>/login</url-pattern>  
  8. </servlet-mapping>  

那么在struts.xml中可以如下配置:

 

java代码:
  1. <result name="toWelcome">/login</result>  

但是请注意,如果这个web资源是另一个Action的话,不能这么配置,需要使用Struts2的另一种名称为“chain”的ResultType。

(3)使用“dispatcher”的ResultType,不能访问其他web应用中的web资源。当然,这个特性是由javax.servlet.RequestDispatcher类的forward方法决定的。

(4)更完整的配置方式

       平时把result对应的jsp的路径,直接作为<result>元素中的文本来配置,这是简化的写法,实际上对于dispatcher还有两个参数可以配置,示例如下:

 

java代码:
  1. <result name="toWelcome" type="dispatcher">  
  2.                 <param name="location">/s2impl/welcome.jsp</param>  
  3.                 <param name="parse">true</param>  
  4. </result>  

location参数就是咱们平时写的下一个jsp的位置,而parse参数决定了location是否可以通过使用OGNL来引用参数,默认为true。

其实,前面使用${folder}来引用Action的folder属性的值的例子,就是使用的OGNL来引用参数。

5.2.4  名称为redirectResultType

1:基本使用

名称为“redirect”的ResultType,在struts-default.xml里的配置如下:

 

java代码:
  1. <result-type name="redirect"   
  2.     class="org.apache.struts2.dispatcher.ServletRedirectResult"/>  

通过配置可以看出,它对应的实现类是ServletRedirectResult。

       这种Result同常也使用JSP作为视图技术。它包装的是javax.servlet.http.HttpServletResponse类的sendRedirect方法,这个ResultType也是用来实现跳转到下一个页面的。

但是它的功能与上面的dispatcher不同,“redirect”的特点是全新的请求,这就意味着,本次请求和跳转到下一个页面的请求是不同的对象,因此它们的值是不一样的。

可以通过如下的方式来测试一下。修改前面dispatcher的示例,主要是修改struts.xml,需要把result的type设置为“redirect”。示例如下:

 

java代码:
  1. <action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">  
  2.             <result name="toWelcome" type="redirect">/s2impl/welcome.jsp</result>  
  3. </action>  

其他的可以不用改变,然后回到登录页面,填写帐号和密码,然后点击登录按钮,正确的跳转到了欢迎页面,如下图所示:

 

图5.1  使用redirect的欢迎页面

仔细看看,有什么不同?

很明显,这里没有正确的显示出帐号的值来,看看后台有值吗?肯定是有的。这就说明经过“redirect”这个ResultType,跳转到欢迎页面的时候,两个请求对象不是一个了,因此在欢迎页面无法获取到帐号的值了。

你可以把struts.xml中,type=“redirect”的配置去掉,或者改成type=“dispatcher”,再次运行看看,欢迎页面就应该有值了。

2:几个小知识点

       对比着dispatcher的ResultType,来看看相应的几个问题。

(1)同样在<result>元素的定义中可以使用Action的execute方法运行之后的数据。

       测试示例,同样在Action中定义一个folder字符串,并在execute中对它赋值,跟前面一样,这里就不去赘述了。

那么,在<result>的定义中就可以引用folder这个变量,示例如下:

 

java代码:
  1. <result name="toWelcome" type="redirect">/${folder}/welcome.jsp</result>  

由于redirect采取重定向的方式,下一个页面会取不到上一个请求对象里面的值,如果要传值的话,可以采用get的方式传参。示例如下:

 

java代码:
  1. <result name="toWelcome" type="redirect">/${folder}/welcome.jsp?account=${account}</result>  

上面这个配置,会向新请求里面传入account的参数,这样在欢迎页面就可以获取到account的值了。

但是,前面写的欢迎页面是取不到这个account的值的,为什么呢?先来看看前面写的欢迎页面取值的那句话,如下:

 

java代码:
  1. 欢迎账号为<s:property value="account"/>的朋友来访  

以前的欢迎页面,是通过使用Struts2的标签来获取的account的值,Struts2的标签会到Struts2的值栈里面去取值,而这里是执行Result的时候,才再请求上添加了account这么一个参数,然后就直接回到页面了,根本不会再走一次Struts2的运行过程,也就是说,这里传递的这个参数,根本不会进入到这个请求对应的值栈,因此这里这个写法是取不到值的。

       那么该怎么写才能获取到这个account参数的值呢?

有两个简单的方法,一个是直接使用Servlet的HttpServletRequest对象的方法来获取参数,另外一个方法是直接使用EL表达式,都是可以的,示例如下:

 

java代码:
  1. 欢迎账号为<%=request.getParameter("account") %>的朋友来访  
  2. 欢迎账号为${param['account']}的朋友来访  

再次测试看看,此时的欢迎页面就应该有account的值了。

(2)对于redirect的使用范围,除了可以配置jsp外,还可以配置其他的web资源,比如其他的Servlet等。具体写法跟dispatcher一样,不再赘述。

(3)使用“redirect”的ResultType,可以访问其他web应用中的web资源,甚至是任何你想要访问的网站都可以。

(4)更完整的配置方式

       与“dispatcher”一样,“redirect”也可以配置<param>,同样可以配置location和parse,连含义都是一样的,因此就不去示例了。

5.2.5  名称为chainResultType、

1:基本使用

名称为“chain”的ResultType,在struts-default.xml里的配置如下:

 

java代码:
  1. <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吧,稍微简化一下,示例如下:

 

java代码:
  1. public class HelloWorldAction extends ActionSupport {  
  2.     private String account;  
  3.     private String password;  
  4.     private String submitFlag;  
  5.       
  6.     public String execute() throws Exception {  
  7.         this.businessExecute();  
  8.         return "toSecond";  
  9.     }  
  10.     public void businessExecute(){  
  11.         System.out.println("用户输入的参数为==="+"account="+account+",password="+password+",submitFlag="+submitFlag);  
  12.     }  
  13. //属性对应的getter/setter方法,省略了  
  14. }  

第二个Action,示例代码如下:

 

java代码:
  1. public class SecondAction extends ActionSupport {     
  2.     public String execute() throws Exception {  
  3.         System.out.println("现在SecondAction进行处理");  
  4.         return "toWelcome";  
  5.     }  
  6. }  

然后到struts.xml中,配置这两个Action,要注意第一个Action的配置,在配置“toSecond”这个result的时候,用的就是chain这个ResultType,示例如下:

 

java代码:
  1. <package name="helloworld"  extends="struts-default">  
  2.         <action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">  
  3.             <result name="toSecond" type="chain">  
  4.                 <param name="actionName">secondAction</param>  
  5.             </result>  
  6.         </action>  
  7.         <action name="secondAction" class="cn.javass.action.action.SecondAction">  
  8.             <result name="toWelcome">/s2impl/welcome.jsp</result>  
  9.         </action>  
  10.     </package>  

其他的可以不用改变,然后回到登录页面,填写帐号和密码,然后点击登录按钮,正确的跳转到了欢迎页面,如下图所示:

图5.2  使用chain的欢迎页面

仔细观察上面的欢迎页面,会发现能够访问到用户在登录页面填写的帐号数据。再来看看后台,会输出如下信息:

 

java代码:
  1. 用户输入的参数为===account=test,password=111111,submitFlag=login  
  2. 现在SecondAction进行处理  

其中第一行是HelloWorldAction输出的信息,而第二行,很明显是SecondAction输出的信息了。也就是说,用户提交登陆请求,只发出了一次请求,但是有两个Action来处理了这个请求。这个请求先被HelloWorldAction进行处理,然后链接到SecondAction,由SecondAction来继续处理这个请求,并在处理完成后,产生响应,回到欢迎页面。

2:几个小知识点

(1)chain不能在result配置的时候传递参数,也就是说,不能类似于如下的配置:

 

java代码:
  1. <result name="toSecond" type="chain">  
  2.             <param name="actionName">secondAction?account=5</param>  
  3.         </result>  

这种配置方式是不行的,因为这里要求配置的是要链接的Action的name,不能传递参数,那么,要传递参数怎么办呢?

       那就需要在Action里面使用ActionContext或者ServletActionContext,由于还没有学到,这里先不去示例了。

(2)在上面示例的欢迎页面,照样输出了用户在登录页面提交给HelloWorldAction的值,这说明使用chian的方式,后面的Action会和前面的Action共用同一个ActionContext,简单点说就是有同样的数据。

(3)名称为“chain”的ResultType在配置的时候,除了前面示例中的actionName外,还有一个参数,名称为“namespace”,表示被链接的Action所在包的命名空间,如果不设置,默认的即是当前的命名空间。配置示例如下:

 

java代码:
  1. <result name="toSecond" type="chain">  
  2.                 <param name="actionName">secondAction</param>  
  3.                 <param name="namespace">其他Package的namespace</param>  
  4.             </result>  

5.2.6  名称为freemarkerResultType

Struts2除了支持以JSP作为视图技术之外,还支持其他的模板技术,比如FreeMarker和Velocity。这里仅以FreeMarker为例,来讲述一下Struts2如何使用其他模板技术。

FreeMarker是一个纯Java模板引擎,是一种基于模板来生成文本的工具。具体的FreeMarker的知识这里就不去介绍了,感兴趣的朋友可以到http://freemarker.sourceforge.net/去获取详细的信息。

freemarker的ResultType,顾名思义,就是用来处理结果页面为使用FreeMarker制作的页面的这样一种ResultType。在struts-default.xml中,名称为“freemarker”的ResultType的配置如下:

 

java代码:
  1. <result-type name="freemarker"   
  2.     class="org.apache.struts2.views.freemarker.FreemarkerResult"/>  

下面通过一个简单的示例,来看看如何使用名称为freemarker的ResultType。

1:沿用前面的资源

       HelloWorldAction和login.jsp都和前面的示例一样,这里就不再赘述了。

2:制作Freemarker的页面welcome.ftl

       这个页面很简单,只是简单的输出帐号信息,示例代码如下:

 

java代码:
  1. <html>  
  2. <head>  
  3. <meta http-equiv="Content-Type" content="text/html; charset=gb2312">  
  4. <title>Insert title here</title>  
  5. </head>  
  6. <body>  
  7. 欢迎账号为${account}的朋友来访  
  8. </body>  
  9. </html>  

这个页面就像一个普通的jsp,其中的${account},就像在jsp中调用el表达式一样。而其中的内容,就像以前在welcome.jsp中使用el表达式${ account }取值一样。

这里要注意的一个问题是,究竟这个welcome.ftl放在什么地方?

FreeMarker对应的ftl文件,并不是放在WebContent下,而是放在classes的路径下,在Eclipse中开发的时候,放到src下,因为Eclipse会自动将其编译到classes路径去。

3:配置struts.xml

主要是要修改toWelcome的result配置,示例代码如下:

 

java代码:
  1. <action name="helloworldAction" class="cn.javass.action.action.HelloWorldAction">  
  2.             <result name="toWelcome"  type="freemarker">welcome.ftl</result>  
  3. </action>  

注意其中的type是freemarker,对应的值是“welcome.ftl”,意味着在classes的根下有一个“welcome.ftl”的文件。如果对应的值是“/aa/ welcome.ftl”,那就表明在classes的根下有一个名称为“aa”的文件夹,在aa文件夹下有一个名为“welcome.ftl”的文件

4:关于freemarker的配置

freemarker类型Result的共有4个参数,示例如下:

 

java代码:
  1.         <result name="toWelcome"  type="freemarker">  
  2.         <param name="location">welcome.ftl</param>  
  3. <param name="parse">true</param>  
  4. <param name="contentType">text/html</param>  
  5. <param name="writeIfCompleted">false</param>  
  6.         </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即可,示例如下:

 

java代码:
  1.     <package name="hello"  extends="struts-default">  
  2.         <result-types>  
  3.             <result-type name="freemarker"   
  4.                 class="org.apache.struts2.views.freemarker.FreemarkerResult"   
  5.                 default="true"/>  
  6.         </result-types>     
  7.         <action ……>  
  8.             <result name="toWelcome"  type="freemarker"> welcome.ftl  
  9.                                   
  10.             </result>  
  11.         </action>          
  12. </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获取更多内容

猜你喜欢

转载自rubbing-struts2.iteye.com/blog/1494238