[2011-5-8]
这次项目需要对一个URL进行重写,一个简单的方法就是针对本次的应用对请求的URL做一个特殊的处理。但是为了以后扩展方便,我把他写成了一个通用的URLRewrite工具类,理论上是支持各种框架的。项目的地址是:http://code.google.com/p/eagle-beak/, GPLv2协议,有兴趣的可以研究一下。
这里我给大家举个例子如何使用我的URLRewrite工具,以最基础的JavaEE做为例子。大家都知道,在JavaEE中要对请求进行处理自然要选择Filter,所以我们就把URLRewrite的工作放在Fitler 之中,如本实例的URLRewriteFilter,当然这里我们需要用“/*”来拦截所有的请求,以进行URL分析。URLRewriteFilter的代码如下:
package org.sefler.test;
import java.io.IOException;
/**
* Servlet Filter implementation class URLRewriteFilter
*/
public class URLRewriteFilter implements Filter {
private static final Logger LOG = Logger.getRootLogger();
private static URLRewriteConfiger CONFIG;
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
LOG.debug("Requesting: " + httpServletRequest.getServletPath());
URLRewriter rewriter = CONFIG.getURLRewriter("test");
try {
URLRequest urlRequest = rewriter.rewrite(httpServletRequest.getServletPath());
RequestDispatcher dispatcher = request.getRequestDispatcher(urlRequest.getTarget());
dispatcher .forward(new ModifiableHttpServletRequest(httpServletRequest, urlRequest.getQueries()), response);
LOG.debug("Redirecting to " + urlRequest.getTarget());
} catch (NoURLMappingFoundException e) {
// do not rewrite
LOG.debug("No matching patterns were found, will do nothing");
chain.doFilter(request, response);
} catch (URLRewriteException e) {
LOG.error("Error when rewriting URL in URLRewriteFilter, Reuqest URL: "
+ httpServletRequest.getServletPath(), e);
}
}
/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
try {
String configPath = fConfig.getInitParameter("configFile");
URLXMLConfiger.getInstance().init(fConfig.getServletContext().getResourceAsStream(configPath));
CONFIG = URLXMLConfiger.getInstance();
} catch (MalformedConfigException e) {
LOG.error("Error when initializing filter: " + getClass().toString(), e);
}
CONFIG = URLXMLConfiger.getInstance();
}
@Override
public void destroy() {}
}
请注意里面的init方法,在这个方法中我们发现以下两行,这里需要初始化URLRewrite的配置,我们的URLRewrite支持各种各样的配置,这里我们使用的是XML的方式进行配置的。
String configPath = fConfig.getInitParameter("configFile");
URLXMLConfiger.getInstance().init(fConfig.getServletContext().getResourceAsStream(configPath));
XML文件的内容如下:
<?xml version="1.0" encoding="utf-8"?>
<config xmlns="http://www.taobao.com/wing/urlrewrite" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.taobao.com/wing/urlrewrite urlrewrite.xsd ">
<rewriter name="test" type="regex" >
<mapping>
<target>/index.jsp</target>
<pattern>/123.htm</pattern>
<handler>org.sefler.test.SimpleTestHandler</handler>
</mapping>
<mapping>
<target>/WEB-INF/viewArticle.jsp</target>
<pattern>/a[0-9]+.htm</pattern>
<handler>org.sefler.test.ArticleHandler</handler>
</mapping>
</rewriter>
</config>
好了,配置好了以后,我们就可以在Filter里面实现我们的大计了。从刚才的XML配置文件我们也看到了,对个mapping对应一个pattern(即客户端请求的URL模式)、target(即最终需映射到的服务器的页面)与一个handler,handler是需要用户自己来实现的。我们现在先来一个简单的,它要做的只是把index.jsp重写成123.htm,那么配置就是我们的第一个Mapping写的那样了,我们使用一个名为SimpleTestHandler的Handler来处理这个重写,它的代码如下:
package org.sefler.test;
import java.util.Collections;
import java.util.Map;
import com.taobao.matrix.eagle.beak.URLMapping;
import com.taobao.matrix.eagle.beak.URLRequest;
import com.taobao.matrix.eagle.beak.URLRewriteHandler;
public class SimpleTestHandler implements URLRewriteHandler {
@SuppressWarnings("unchecked")
@Override
public URLRequest parse(String requestPath, URLMapping mapping) {
URLRequest urlRequest = new URLRequest(mapping.getTarget(), Collections.EMPTY_MAP);
return urlRequest;
}
@Override
public String render(Map<String, Object> queries, URLMapping mapping) {
return "No render rule defined! Will do nothing!";
}
}
可以看出这个Handler什么都没有做,只是把pattern映射成了target。那么最终的结果就如下:
OK, 现在我们来弄个更复杂的,现在我们的URL Patter的为a1234.htm,后面的数字是隐藏的参数,取名arNo。我们使用ArticleHandler来处理这个请求
package org.sefler.test;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.log4j.Logger;
import com.taobao.matrix.eagle.beak.URLMapping;
import com.taobao.matrix.eagle.beak.URLRequest;
import com.taobao.matrix.eagle.beak.URLRewriteHandler;
import com.taobao.matrix.eagle.beak.URLUtil;
public class ArticleHandler implements URLRewriteHandler {
public static final Logger LOG = Logger.getLogger("test");
@Override
public URLRequest parse(String requestPath, URLMapping mapping) {
int arNo = NumberUtils.toInt(URLUtil.getFileName(requestPath).substring(1), 0);
LOG.debug("Parsed arNo parameter: " + arNo);
Map<String, Object> queries = new HashMap<String, Object>();
queries.put("arNo", arNo);
URLRequest urlRequest = new URLRequest(mapping.getTarget(), queries);
return urlRequest;
}
@Override
public String render(Map<String, Object> queries, URLMapping mapping) {
if (queries.size() == 0 || queries.get("arNo") == null) {
return "index.jsp";
}
String renderedURL = "a" + queries.get("arNo") + ".htm";
LOG.debug("Rended URL: " + renderedURL);
return renderedURL;
}
}
需要注意的是,JavaEE的HttpServletRequst并不充许你更改请求的参数,所以我们需要用HttpServletRequestWrapper进行wrap一下,具体怎么wrap 大家可以看最后附带的源码,这里也不多说了。至于重写URL的生成,比如在JSP里使用“viewArticle.jsp?arNO=123”这类的href自然希望渲染成“a123.htm”。由于JavaEE并没有专门负责URL的模块,这里们可以自己建一个应用级的URLBroker来负责生成想要的URL。例如在viewArticle.jsp下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<jsp:useBean id="urlBroker" class="org.sefler.test.URLBroker" scope="request" />
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>View Article</title>
</head>
<body style="font-family: Arial">
<div>
You're viewing: <span style="color: green">viewArticle.jsp</span>
</div>
<div>
Requesting article No.: <span style="color: green"><%= request.getParameter("arNo") %></span>
</div>
<div style="background-color: lightblue;">
<% int arNo = org.apache.commons.lang.math.NumberUtils.toInt(request.getParameter("arNo"), 0) + 1; %>
Click <a href="<%= urlBroker.setTarget("/WEB-INF/viewArticle.jsp").addParameter("arNo", arNo).render() %>"> here</a>
to view next article
</div>
</body>
</html>
只要把URLBroker作为JavaBean注入就可以。当然,我这里只是提供一种简单的方法,肯定有更优雅的方法,大家可以照着这种思想去想一想(偶们公司的框架解决这个问题轻而易举,^_^)。话说,某个开源项目与我的思想很像(好吧,我与它的很像吧),不过我想说的是,有些问题由于限制问题,可选用的解决方案也不多,大家的解决方案自然也就很像了~