webx

<pipeline>
    <valve class="com.alibaba.service.pipeline.TryCatchFinallyValve">
        <try>
            <valve class="com.alibaba.turbine.pipeline.SetLoggingContextValve"/>
            <valve class="com.alibaba.turbine.pipeline.SetLocaleValve"
defaultLocale="zh_CN" defaultCharset="GBK"/>
            <valve class="com.alibaba.turbine.pipeline.AnalyzeURLValve"/>
            <valve class="com.alibaba.turbine.pipeline.ChooseValve" label="processModule">
                <when extension="jsp, vm">
                    <valve class="com.alibaba.turbine.pipeline.PerformActionValve"

actionParam="action"/>
                    <valve class="com.alibaba.turbine.pipeline.PerformScreenTemplateValve"/>
                </when>
                <when extension="do">
                    <valve class="com.alibaba.turbine.pipeline.PerformActionValve"

actionParam="action"/>
                    <valve class="com.alibaba.turbine.pipeline.PerformScreenValve"/>
                </when>
            </valve>
            <valve class="com.alibaba.turbine.pipeline.RedirectTargetValve" goto="processModule"/>
        </try>
        <catch>
            <valve target="error.vm" class="com.alibaba.turbine.pipeline.SetErrorPageValve"/>
            <valve class="com.alibaba.turbine.pipeline.PerformScreenTemplateValve"/>
        </catch>
        <finally>
            <valve class="com.alibaba.turbine.pipeline.SetLoggingContextValve" action="cleanup"/>
        </finally>
    </valve>
</pipeline>




PerformActionValve
PerformActionValve用来执行action。
<valve class="com.alibaba.turbine.pipeline.PerformActionValve" actionParam="action"/>
参数actionParam的值指明了valve将根据什么参数来确定action的名称。
例如,假如URL为http://localhost:8080/workshop/hello.htm?action=myAction,那么valve将会执行MyAction类


有关action的细节,我们将在后面展现。

PerformScreenTemplateValve
PerformScreenTemplateValve用来执行screen和模板。
<valve class="com.alibaba.turbine.pipeline.PerformScreenTemplateValve"/>
假如target的值为:/xxx/yyy/hello.vm,那么,valve会:
1. 在/templates/screen目录下,找到/xxx/yyy/hello.vm模板。
2. 依次查找screen类:
a) xxx.yyy.Hello (如果找不到,尝试下一个)
b) xxx.yyy.Default (如果找不到,尝试下一个)
c) xxx.Default (如果找不到,尝试下一个)
d) Default (如果找不到,尝试下一个)
e) TemplateScreen (系统默认screen,不可能找不到的)
3. 执行screen,并渲染screen模板。
4. 如果存在layout布局,则渲染layout,并将screen放于布局中。
关于layout布局,后面会讲到。


PerformScreenValve
和前一个valve不同,PerformScreenValve不关心模板,只关心screen类。
<valve class="com.alibaba.turbine.pipeline.PerformScreenValve"/>
假如target的值为:/xxx/yyy/hello.do,那么,valve会:
1. 查找screen类:xxx.yyy.Hello
2. 执行screen类。
3. 如果存在layout布局,则渲染layout,并将screen放于布局中。


.RedirectTargetValve
<valve class="……" label="myLabel">
……
<valve class="com.alibaba.turbine.pipeline.RedirectTargetValve" goto="myLabel"/>
RedirectTargetValve实现了内部重定向。
如果在screen、action里调用了rundata.setRedirectTarget(),那么,RedirectTargetValve会将控制转向到标记

了label的valve上,从而实现内部的循环。在上面的例子中,PerformActionValve和PerformScreenTemplateValve

(或PerformScreenValve)被重新执行了,但target的值因setRedirectTarget()而改变,因而实现了内部重定向




SetErrorPageValve
<valve class="com.alibaba.turbine.pipeline.SetErrorPageValve" target="error.vm"/>
SetErrorPageValve的工作是显示错误页面error.vm。



系统怎样找到SiteUserAction类呢?
你需要修改一下webx.xml配置,告诉系统到何处去找类:
<configuration>
    <services>
        ……
        <service name="ModuleLoaderService">
            <property name="module.packages">
                <value>com.alibaba.workshop2.module</value>
            </property>
        </service>
    </services>
</configuration>
这段配置告诉Webx:当action=site_user_action时,就执行

com.alibaba.workshop2.module.action.SiteUserAction类。
(注:若
<value>com.alibaba.workshop2.module.action</value>,则访问
com.alibaba.workshop2.module.action.action.SiteUserAction类

<input type="hidden" name="action" value="web/site_user_action"/>,则访问
com.alibaba.workshop2.module.action.web.SiteUserAction类
)

内部重定向:
rundata.setRedirectTarget("viewUser.vm");
外部重定向:

String viewNewUser = "http://localhost:8080/workshop/view_user.htm?id="
                                     + user.getId() + "&new_user=true";

            try {
                rundata.setRedirectLocation(viewNewUser);
            } catch (IOException e) {
                throw new WebxException("Failed to redirect to " + viewNewUser, e);
            }

9.4.内外重定向的比较
内外重定向有什么区别呢?何时使用内部重定向,何时使用外部重定向呢?
1. 实现方法不同
内部重定向是通过RedirectTargetValve配合rundata.setRedirectTarget()方法实现的。它是一个HTTP请求内部的

流转。
而外部重定向是通过rundata.setRedirectLocation()方法,或者rundata.getResponse().sendRedirect()方法实

现的。它需要浏览器的配置,通过两个HTTP请求才能完成的流转。
此外,由于外部重定向涉及到和浏览器的I/O,因此需要捕获IOException。
2. 共享对象不同
由于内部重定向是在一次请求中完成的,因此重定向前后的模块可以共享一些对象,例如:context、rundata等。
而外部重定向是在两次请求中完成的,因此重定向前后的模块无法共享对象。
3. 重定向的目标不同
内部重定向需要指定target,例如:rundata.setRedirectTarget(“viewUser.vm”)。如前所述,target是经过

AnalyseURLValve之后的产物,它是一种内部的状态。
而外部重定向必须指定完整的URL,例如:rundata.setRedirectLocation(“http://...”)。
不知你是否感受到前述外部重定向代码的不良设计?我们将URL硬编码到程序里了 —— 如果URL被改变,例如,

hostname、端口、应用的名称等发生改变,那么,就需要修改程序并重新编译了;此外,通过这种形式创建参数也

很不容易:"…?id=" + user.getId() + "&new_user=true"。我们后面将提出解决的方案。
4. 重复提交的问题
由于浏览器无法感知内部重定向,因此当用户点击浏览器的刷新按钮时,浏览器会重新发送用户数据给第一个页面

,从而导致重复提交数据的问题。这是一种常见的问题。
但是外部重定向可以部分地避免这个问题。因为重定向后的第二个页面,和重定向前的第一个页面无关,因此当用

户按下刷新按钮时,只会刷新第二个页面,不会导致第一个页面的数据被重新提交。外部重定向是解决重复提交问

题的一个简易方法 —— 要完全防止重复提交,必须使用session技术(这已经超出了本文的范围)。


12.URL的生成与解析
记得我们在做外部重定向时,是如何构造URL的吗?
String viewNewUser = "http://localhost:8080/workshop/view_user.htm?id="
                         + user.getId() + "&new_user=true";

rundata.setRedirectLocation(viewNewUser);
这种硬编码的URL有很多缺点:
 不能应付环境的变化:hostname、端口、context path(/workshop)、参数等都有可能变化。一但变化

,就要修改代码并重新编译。
 难以组装参数:通过字符串的拼接来组装参数十分笨拙。特别是当参数的值包括一些特殊字符时,很容易

忘记对其进行URL encoding,转换成%20%3C之类的形式。
 没有一个集中的地方控制URL。当一个URL被改变时,需要修改程序 —— 很容易漏掉一些程序。
在Webx中,有一个专门用来生成URL的服务:URI Broker Service,能够解决这些问题。下面,让我们利用URI

Broker Service来改进应用。
12.1.配置URIBrokerService
12.1.1.修改webx.xml
请修改webx.xml,增加URIBrokerService的配置:
<configuration>
    <services>
        ……
        <service name="URIBrokerService">
            <property name="uri.descriptors">
                <value>/WEB-INF/uri.xml</value>
            </property>
        </service>
    </services>
</configuration>
12.1.2.创建uri.xml
为了使用URI Broker Service,你需要创建一个简单的URI配置文件:/WEB-INF/uri.xml。内容如下:
<?xml version="1.0" encoding="GB2312"?>
<uri-config>
    <!-- ================================================================ -->
    <!-- 外部链接。                                                       -->
    <!-- ================================================================ -->
    <uri name="alibabaSite" expose="true">
        <serverURI>http://china.alibaba.com/</serverURI>
    </uri>
    <!-- ================================================================ -->
    <!-- 内部链接。                                                       -->
    <!-- ================================================================ -->
    <turbine-uri name="workshopModule" expose="true"/>
    <turbine-content-uri name="workshopContent" expose="true" extends="workshopModule"/>
    <turbine-uri name="viewNewUser" expose="true" extends="workshopModule">
        <target>viewUser.vm</target>
        <query id="newUser">true</query>
    </turbine-uri>
</uri-config>


12.2.在程序中使用URIBrokerService
接下来,请修改SiteUserAction类,使之使用URIBrokerService来生成URL。
public abstract class SiteUserAction extends TemplateAction {
    ……
    protected abstract URIBrokerService getURIBrokerService();

    public void doRegister(RunData rundata, TemplateContext context)
            throws WebxException {
        ……

        URIBroker broker = getURIBrokerService().getURIBroker("viewNewUser", rundata);

        broker.addQueryData("id", user.getId());

        String viewNewUser = broker.render();

        ……
        rundata.setRedirectLocation(viewNewUser);
        ……
    }
}
在程序中使用URIBrokerService分成以下几个步骤:
1. 取得URIBrokerService的实例。在Action/screen/control中,可以用“注入”的方式来取得:
protected abstract URIBrokerService getURIBrokerService();
2. 从URIBrokerService中,取得URIBroker实例。
URIBroker broker = getURIBrokerService().getURIBroker("viewNewUser", rundata);
其中,"viewNewUser"就是uri.xml中定义的一个URL:
<turbine-uri name="viewNewUser" expose="true" extends="workshopModule">
    <target>viewUser.vm</target>
    <query id="newUser">true</query>
</turbine-uri>
 <turbine-uri>定义了一个TurbineURIBroker类的实例。
 <turbine-uri extends="workshopModule">指明该URL从workshopModule URL继承所有的属性,包括

hostname、port、context path等等。因此当前URL将在此基础上被继续构造:http://localhost:8080/workshop


 <target>viewUser.vm</target>定义了target=viewUser.vm。该target将被追加到URL的末

尾:/view_user.htm。
 <query id="newUser">true</query>定义了一个参数newUser=true。该参数将被加入到URL的末尾:?

newUser=true。
因此,当broker对象从URIBrokerService中被取出时,就已经代表了如下的URL:

http://localhost:8080/workshop/view_user.htm?newUser=true。最重要的是,作为程序代码,它并不关心URL长

成什么样子。
3. 追加ID参数。
broker.addQueryData("id", user.getId());
以这种方式追加的参数,你不用担心其中包含了URL禁止使用的字符,如空格、加号、&号等等。URIBroker会自动

对所有参数进行URL encoding编码,将禁用字符转换成%20、%3C之类的形式。
4. 生成最终URL。
String viewNewUser = broker.render();
这样就生成了最终的URL:http://localhost:8080/workshop/view_user.htm?newUser=true&id=baobao。

在模板中使用URIBrokerService:
在模板中使用URIBrokerService比在程序中使用更简单 —— 你可以直接访问在uri.xml中被标记成expose=true的

所有URL定义。

<img src="$workshopContent.setContentPath("images/logo_webx.png")"/>
<h1>标题、Logo</h1>
在uri.xml中,workshopContent定义了一个<turbine-content-uri>(即TurbineContentURIBroker)对象。利用该

URL,可以创建指向WEB应用目录下的资源,例如图片、CSS、JavaScript等。
由于我们将workshopContent设置成了expose=true,因此可以在模板中直接引用$workshopContent对象

你可能感兴趣的:(Web)