引入:

关于UrlRewriteFilter大家也许都不陌生,尤其那些做过SEO(搜索引擎优化)的同学们都知道,它会吧一个url重写为另外一URL,为什么要这么做呢?因为搜索引擎看待动态的URL非常不舒服(所谓动态URL就是?关联queryParameter,&关联参数对这种url,比如http://1.2.3.4:3333/badurltest?a=1&b=2&c=3这种url.而我们重写后会把它改为静态url,比如http://1.2.3.4:3333/badurltest_1_2_3.html,这种情况下搜索引擎看的比较舒服。


那么我们看下Liferay中如何做到url重写的,当然了,肯定还是按照惯例一样,它会用filter来完成这个工作。



调试分析:

熟悉urlrewrite的人都知道,它一般会采用一个urlrewrite.xml来完成重写的规则配置,我们liferay也不例外。它在$LIFERAY_TOMCAT_HOME/webapps/ROOT/WEB-INF目录下:

Liferay中的UrlRewriteFilter的研究_第1张图片

我们选取一个最简单的,比如http://172.29.175.236:8080/tunnel-web ,看它是否能重写为http://172.29.175.236:8080/api


很显然,这个URLRewriteFilter是被InvokerFilterChain调用的。我们进入doFilter()方法:因为我们的拿到的sourcecode中的URLRewriteFilter.java无法和jar包中的UrlRewriterFilter.class相匹配,所以我们只能用jd-gui反编译来对照着看。


跳去不重要的行,我们目光停在了第726到728行。

Liferay中的UrlRewriteFilter的研究_第2张图片

首先,第726行会去利用proce***equest(HttpServletRequest,HttpServletResponse)来获取重写url.我们看它的实现,首先它会在第68行把它trim()下去掉首尾空白字符:

100938898.png

trim()完之后,这个url还是/tunnel-web:

101105952.png


然后,在84行将其用utf-8形式将其decode,因为我们都知道最早我们输入的url都被URLEncode过了。

101255709.png

decode完之后,这个originalUrl仍然是/tunnel-web:

101358524.png


然后第95到98行会从请求url中吧contextPath部分移除。

101517580.png

因为如下图所示,我们的contextPath为"",所以98行执行完之后originalUrl依然是/tunnel-web:

Liferay中的UrlRewriteFilter的研究_第3张图片


从102-107行会吧queryString加载originalUrl的后面:

Liferay中的UrlRewriteFilter的研究_第4张图片

因为我们没有查询字符串,所以不加任何东西。


然后第123行会获取所有的url重写规则并且保存在变量rules中,它会从urlrewrite.xml中获取所有规则配置信息:

102215278.png

因为最上面,我们在urlrewrite.xml中配置了6条规则,所以从调试信息看,这里rules数组变量有6个元素。

Liferay中的UrlRewriteFilter的研究_第5张图片


然后从131-141行就会依次遍历这些规则集然后调用规则来重写请求url了,需要注意的是,它是顺序的执行匹配然后重写的,也就是下一个规则会用上一个规则的输出作为输入:

Liferay中的UrlRewriteFilter的研究_第6张图片

而重写的过程在第135行 execute方法中:


重点:execute()方法的细节:

我们来看下我们的例子,因为我们定义了以下规则:

102732364.png

所以,当请求url /tunnel-web时候,当执行到上述第134行时候,它先从urlrewrite.xml中获得了当前规则(匹配上述定义):

Liferay中的UrlRewriteFilter的研究_第7张图片


然后第135行会调用rule.execute()方法来具体的执行重写:

Liferay中的UrlRewriteFilter的研究_第8张图片

它先在第74行通过executeBase()方法获取一个RuleExecutionOutput对象,具体不展开了,就是创建一个Matcher对象,如果匹配,就把中的()中的匹配正则表达式的重写内容按照顺序1,2,3等替换为中的重写参数$1,$2,$3等。最终通过调试信息我们可以看出,它计算出了最终的匹配目标:Liferay中的UrlRewriteFilter的研究_第9张图片

这里看出,它的replaceUrl 包含了要匹配到的目标,而ruleMatchedtrue表明通过检验,这个url重写规则匹配当前的url.


execute()方法最终会调用rewriteRequest()方法来具体的进行重写:

Liferay中的UrlRewriteFilter的研究_第10张图片

从这里可以看出,因为我们的to type字符串设置为"permanent-redirect",所以我们根据以下规则:

Liferay中的UrlRewriteFilter的研究_第11张图片

我们的toType被设为2.


所以会执行rewriteRequest的第99-103行,最终会吧rewrittenRequestpermanentRedirect设置为true.

104849610.png


然后到回到UrlRewriter的proce***equest()方法中,因为我们已经从execute()方法中返回RewrittenUrl,它的target/api:

Liferay中的UrlRewriteFilter的研究_第12张图片

所以第139行会取出这个target,并且作为最后finalToUrl.

105735395.png



总结:

虽然这片调试没有什么技术难度,不过我们还是可以总结出一些有价值的结论:

(1)Liferay的Url重写是通过UrlRewriteFilter来完成的,并且这个UrlRewriteFilter会被InvokerFilterChain所调用。

(2)重写规则会定义在$LIFERAY_TOMCAT_HOME/webapps/ROOT/WEB-INF/urlrewrite.xml文件中。

(3)执行重写的过程如下:首先它会去吧请求url trim()下,再将其utf-8解码成原始url,然后会去除contextPath部分并且后面加上查询字符串部分,得到重写前的输入url。然后,它会吧urlrewrite.xml中的所有重写规则都读取并且存放在Rule数组中,遍历数组对于重写前url一条一条应用程序规则,并且这是递归的,就是一条规则的输出就是下一条规则的输入。直到吧所有的规则全部应用完,才得到最终重写后的输出url.

(4)在应用重写规则时候,它又分为2部分,首先它会在基类(RuleBase)中应用一般规则替换,就是把所有中的用括号括出来的正则表达式匹配的请求内容转为中重写参数指定的模式。其次,它会去读取中的type属性,并且根据type属性的取值来决定对于重写后的url 进行如何后处理。

(5)type属性有6种,分别是redirect,permanent-redirect,temporary-redirect,pre-include,post-include,forward,我们可以根据不同需要为其进行定制。