进一步优化网站URL
发表于:2010-08-08 | 2 个回复

  在《中小网站静态化目录结构设计》中我们以 Struts2 为例简单地介绍了通过后台解析 url 转向到 html 文件真实地址的方法。由于 Struts2 参数传递方式让 url 看上去不是很友好,那么今天我们还是按照这个思路,打造友好的 url 。
  UrlRewrite 功能强大的 url 工具,它可以根据喜好,自由地设计 url 。由其需要说明的是,通过 UrlRewrite 的配置功能,我们可以为网站设计不同的 url 。关于 UrlRewrite 的配置请参见官方网站 http://www.tuckey.org/urlrewrite/(不过最近好像被河蟹了,不要紧大家可以通过 http://www.web4proxy.com/ 来访问)。

  继续以《中小网站静态化目录结构设计》中的例子为例,Struts2 解析的 url 格式为:http://www.mxjava.com/blog.do?uid=hiswing&id=20100807100 “?”号后面跟着的是参数,这样的 url 用在企业内部系统中还可以,但在以 seo 为上的互联网世界里,显然不是很友好,我们需要对它进行改进。

  http://www.mxjava.com/hiswing/blog/20100807/100.html

  在博客系统中这样格式的 url 看上去很舒服,网站域名后面紧跟用户的 id (当然也可以像新浪微博那样让用户指定博客地址)表明网页属于哪个博客的。接下来 blog 是功能 id (很多博客系统改进成了个人网站,不但有博客还有相册、微博、圈子等功能,这里的功能 id 就是区分用的)表明网页属于哪个功能下的。然后跟 20100807 表明网页的创建时间,最后 100 是日志的 id。接下来要做的就是在 urlrewrite.xml 中配置 url 转向,如下:

<urlrewrite>
	<rule enabled="true">
		<note>
			mxjava.com
		</note>
		<from>^/([a-zA-Z0-9]+)/(.*)/([0-9]+)/([0-9]+).html$</from>
		<to type="forward">/html/$2/$3/$1$4.html</to>
	</rule>
</urlrewrite>

  from 是来源 url 的匹配格式(关于正则表达式,请查询相关资料),用括号括起来表明是一个变量,可以在 to 中用 $1 这样的形式引用。简单分析一下这个配置:

([a-zA-Z0-9]):对应 url 中的 hiswing,在 to 中用 $1 引用。
(.*):对应 url 中的 blog,在 to 中用 $2 引用。
([0-9]+):对应 url 中的 20100807,在 to 中用 $3 引用。
([0-9]+).html:对应 url 中的 100.html,在to 中用 $4 引用。

  UrlRewrite 对 from 和 to 处理后,to 将匹配为:/html/blog/20100807/hiswing100.html,即转向到网站根目录/html/blog/20100807/hiswing100.html 文件。

  可能我们忽略了一个问题,还记得在《》中我们是如何设计目录结构的吗?年/月/日是不同的目录,按照现在的配置年月日是同一目录,这样又会发生目录“爆炸”。又回到最初的老路子上来,这是我们不愿看到的。解决方法有三种,从最简单的开始吧:

1、将 url 形式改为:http://www.mxjava.com/hiswing/blog/2010/08/07/100.html ,并对配置文件做出相应该的改正。这是最笨也是最简单的方法,如果你不介意这样做的话。

2、创建 UrlAction ,在 execute 方法中取得并解析 url ,将解析后的用户id、功能id、年、月、日、日志 id 保存到 request 中,配置 struts.xml 的 result 为: ${功能id}/${年}/${月}/${日}/${用户id}${日志id}.html。修改 urlrewrite.xml 配置为:

<urlrewrite>
	<rule enabled="true">
		<note>
			mxjava.com
		</note>
		<from>^/([a-zA-Z0-9]+)/(.*)/([0-9]+)/([0-9]+).html$</from>
		<to type="forward">/urlaction.do?uid=$1&amp;funid=$2&amp;ymd=$3&amp;aid=$4</to>
	</rule>
</urlrewrite>

  通过 UrlRewirte 转向到 Struts2 ,由 Action 将年月日分解,并定向到 html 文件。这个方法也很简单,也很实用,容易实现。如果你不是完美主义者的话,这个方案应该是不错的选择。不过 struts2 servlet + Action 的处理会比第一种方法多花费一点点时间(性能瓶颈在于数据库,而 servlet 和这个简单的 Action 处理所花的时间可以忽略不计)。

3、如果你不愿意看系统连续进入 UrlRewrite 和 Struts2 ,仅想通都 UrlRewrite 就转向到 html 文件,那么就必需了解 UrlRewrite 的方法。UrlRewrite 可以在配置文件中配置方法,如:

<to type="forward">/${upper:hello}</to>

  解析后为:HELLO。内置的方法有:replace、replaceFirst、escape、unescape、lower、upper、trim。遗憾的是,现有的方法并不能满足我们的需求,庆幸的是 UrlRewrite 是开源的,我们可以通过修改来达到目的。下载 UrlRewrite 源码并部署到 Eclipse 中,打开 FunctionReplacer.java 文件,找到 functionReplace 方法,该方法中做了以上方法的实现:

        if ("replace".equalsIgnoreCase(varType) || "replaceAll".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.replaceAll(varSubName);
        } else if ("replaceFirst".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.replaceFirst(varSubName);
        } else if ("escape".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.escape(varSubName);
        } else if ("unescape".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.unescape(varSubName);
        } else if ("lower".equalsIgnoreCase(varType) || "toLower".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.toLower(varSubName);
        } else if ("upper".equalsIgnoreCase(varType) || "toUpper".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.toUpper(varSubName);
        } else if ("trim".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.trim(varSubName);
        } else if ("length".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.length(varSubName);
        } else {
            log.error("function ${" + originalVarStr + "} type '" + varType + "' not a valid type");
        }

  加入我们的处理:

        if ("replace".equalsIgnoreCase(varType) || "replaceAll".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.replaceAll(varSubName);
        } else if ("replaceFirst".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.replaceFirst(varSubName);
        } else if ("escape".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.escape(varSubName);
        } else if ("unescape".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.unescape(varSubName);
        } else if ("lower".equalsIgnoreCase(varType) || "toLower".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.toLower(varSubName);
        } else if ("upper".equalsIgnoreCase(varType) || "toUpper".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.toUpper(varSubName);
        } else if ("trim".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.trim(varSubName);
        } else if ("length".equalsIgnoreCase(varType)) {
            functionResult = StringFunctions.length(varSubName);
        } else if ("splitYmd".equalsIgnoreCase(varType)) {  // splitYmd 自定义的方法
        	functionResult = MyUtils.splitYmd(varSubName);
        } else {
            log.error("function ${" + originalVarStr + "} type '" + varType + "' not a valid type");
        }

  创建 MyUtils.java ,加入如下方法:

	public static String splitYmd(String ymd) {
		String y = ymd.substring(0, 4);
		String m = ymd.substring(4, 6);
		String d = ymd.substring(6);
 
		return y + "/" + m + "/" + d;
	}

  配置 urlrewrite.xml 为:

<urlrewrite>
	<rule enabled="true">
		<note>
			文章/hiswing/blog/20100807/1.html
		</note>
		<from>^/([a-zA-Z0-9]+)/(.*)/([0-9]+)/([0-9]+).html$</from>
		<to type="forward">/html/$2/${<strong>splitYmd</strong>:$3}/$1$4.html</to>
	</rule>
</urlrewrite>

  注意粗体部分。另外需要注意的地方是不能为 Struts2 配置解析 html 后缀的请求,否则 UrlRewrite 处理完后将会进入 Struts2 。

  UrlRewrite 功能非常强大,可以满足我们不同的需求。当然它也不是万能的,也会有一些所不能及的事。庆幸的是它是开源的,我们可以通过扩展来达到我们的目的。

评论留言

  1. 技术的 还是由技术专业负责 ~我就负责内容吧~

  2. 这样确实不错,我博客的url是http://网址/目录/文章固定链接.html