• WebX的url生成及URIBroker的问题和解析

    前提条件

    将WebX应用部署在“/”上,新建一个component名为“happy”。

    其中的happy对应的uri对象配置如下:

    <uris:uri id="server" requestAware="true" />
    <!-- happy -->

    <uris:turbine-uri id="happyModule" exposed="true" extends="server">
    <componentPath>/happy</componentPath>
    </uris:turbine-uri>
    <uris:turbine-content-uri id="happyContent" exposed="true" extends="happyModule" />

    诡异的现象

    当我们分别打开“http://127.0.0.1:8081/happy/”、“http://127.0.0.1:8081/happy/index”和“http://127.0.0.1:8081/happy/index”时,我们发现:

    • 在“http://127.0.0.1:8081/happy/”中,$happyModule和$happyContent都被解析为“http://127.0.0.1:8081/happy/happy”,显然,这不是我们想要的结果。
    • 在“http://127.0.0.1:8081/happy/index”中, $happyModule和$happyContent都被解析为“http://127.0.0.1:8081/happy/index/happy”,这,跟我们想要的相差更多了
    • 在“http://127.0.0.1:8081/happy/index.htm”中, $happyModule和$happyContent都被解析为“http://127.0.0.1:8081/happy”,后面少了个“/”,嗯,还凑合。

    我们先来看一下,为啥上面的若干情况如此诡异呢?考虑以下四个URL:

    • http://127.0.0.1:8081/happy
    • http://127.0.0.1:8081/happy/
    • http://127.0.0.1:8081/happy/index
    • http://127.0.0.1:8081/happy/index.htm

    其实,他们指向的component都是happy,对应的screen都是index。而事实上,WebX也是这么映射到对应的screen的——因为我们能够正常访问页面,而且都是happy下index的内容,对吧。理论上,我们要求WebX对确定的映射内容也返回稳定的渲染结果,而事实却并非如此。为什么呢?我们要深入源代码一探究竟。

    深入源码

    先简单介绍一下,在com.alibaba.citrus.service.uribroker.uri.URIBroker类中,有一个叫path的数组,里面存放了杠与杠之间的各部分。比如/aaa/bbb/ccc/,那么这个数组存放的是[aaa, bbb, ccc]。此类还有一个叫renderPath()的函数,作用是把path中的各个元素组合起来。如上面的数组会组合成“/aaa/bbb/ccc”。看起来不错,不过少了最后的一道杠。

    回到上面的例子,为什么会出现了两次“happy”呢?直接原因,是path数组中存了两个“happy”。根本原因,是因为request.getServletPath()返回了“/happy/”,导致ServletURIBroker类中populateWithRequest()函数误以为当前的servlet是happy。因为在j2ee中,后面处理URI时会把servlet部分切割掉,所以多加了一次“happy”。这是不正确的,因为当前的servlet应该是“/”才对!

    而这个getServletPath()函数,则是com.alibaba.citrus.service.requestcontext.rewrite.impl.RewriteRequestContextImpl#RequestWrapper中定义的。

    论坛已经有人提出相关的问题:http://www.openwebx.org/forum/showthread.php?tid=91&highlight=servletpath。两年过去,仍旧如故。

    解决

    相当于写死了URL(参见http://openwebx.org/forum/showthread.php?tid=478和http://hi.baidu.com/epplera/item/5877681cce410e221994ec67)。

    方法1:

    使用turbine-content-uri,并且增加:<contextPath>Web App的部署目录,一般部署在根目录则填“/”</contextPath>

    方法2:

    <uris:uri id="server" requestAware="false">
    <serverName>127.0.0.1</serverName>
    <serverPort>8081</serverPort>
    </uris:uri>

    还凑合的一道杠

    为什么说“http://127.0.0.1:8081/happy”还凑合呢?因为,Spring框架中,当我们直接访问“http://127.0.0.1:8081/happy”时,Web服务器发现该网址不存在,于是就返回302跳转到“http://127.0.0.1:8081/happy/”去,所以最后那个是可以正常访问的,虽然跟我们想要的还是差了一道杠(差了网址结尾的“/”)。

    不过,一道杠的差别,却不是轻易就能够解决的。话分两头:一方面,想要用turbine-uri在后面加一道简简单单的杠,几乎是不可能的,后文详述原因;另一方面,如果turbine-uri是指向python编写的web app(尤其是用强哥框架,英文名Django框架)的话,就不一定会自动给你补上后面的一道杠,而是冷冰冰地返回一个404错误。

    一道杠的距离

    这时候,再说一下,WebX中,如果要在uri后面加一道杠,究竟有多难?答案是很难。假设我们分别要生成“http://blog.creke.net/tag/linux/”和“http://blog.creke.net/803.html”,注意后面有道杠。看看下面的配置:

    <uris:uri id="crekeServer" requestAware="false" >
    <serverName>blog.creke.net</serverName>
    </uris:uri>

    <uris:turbine-content-uri id="crekeLink1" exposed="true" extends="crekeServer" >
    <contentPath>/tag/linux/</contentPath>
    </uris:turbine-content-uri>
    <uris:turbine-content-uri id="crekeLink2" exposed="true" extends="crekeServer" >
    <componentPath>/tag</componentPath>
    <contentPath>/linux/</contentPath>
    </uris:turbine-content-uri>
    <uris:turbine-uri id="crekeLink3" exposed="true" extends="crekeServer" >
    <target>/tag/linux/</target>
    </uris:turbine-uri>
    <uris:turbine-content-uri id="crekeLink4" exposed="true" extends="crekeServer" >
    <contentPath>/803.html</contentPath>
    </uris:turbine-content-uri>

    生成的结果分别是:

    • http://blog.creke.net/tag/linux
    • http://blog.creke.net/tag/linux
    • http://blog.creke.net/tag/linux
    • http://blog.creke.net/803.html

    根本原因是,uriBroker将任何URL的路径部分都一视同仁地分解为上文所述的path数组。这对于有后缀名的“http://blog.creke.net/803.html”来说无所谓,反正“803.html”是path的一个元素,直接加就对了;但是对于“http://blog.creke.net/tag/linux/”来说,uriBroker并不会记录是否需要补上最后的一道杠,也就是被吞掉了。

    总结

    在WebX中,如果不想在配置中写死URL前缀,那么除了首页外,总是要加上index.htm来标示默认页面。而如果要生成最后带“/”的URL的字符串,则不要使用URIBroker类。

    最后,还是用WebX文档中的一句话来作总结吧:“我并不是说所有的框架都一样好,而是说只要假以时日,所有的框架在发展过程中,必然会积聚好的方面,淘汰坏的方面,从而变得足够好。从这个角度看,的确没有特别明显的理由来选择Webx,但也没有明显的理由不选择Webx。”(via http://openwebx.org/docs/preface.html#d0e76)

    分类: 所谓技术 | 标签: ,,,,