因工作原因,要实现一个动态的栏目,使用独立的地址访问。例如对于项目xxx使用http://localhost:8080/xxx/aaa可访问栏目aaa。
“海词”网站(http://dict.cn/)实现了“地址栏一步查词”,具有类似的功能。
提出了两种方案:
1、建立固定结构目录aaa,可使用要求的地址访问。当建立新栏目时,程序拷贝aaa目录,修改目录名实现。
2、使用过滤器,拦截浏览器对http://localhost:8080/xxx/aaa的访问,并转发某个固定的Servlet,将aaa作为参数传给转发的Servlet中。
使用解决方案1,当程序变更时,需要更改每一个目录下的程序,所以优先尝试方案2。
首先建立一个Servlet,为了简单模拟动态页面,只是读取一个参数,并打印到页面中
public class HostTestServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String msg = request.getParameter("id"); PrintWriter out = response.getWriter(); out.print(msg); out.flush(); out.close(); } }
对于过滤器来说,要拦截所有请求并进行地址分析,只有满足条件的才进行转发,这里的条件就是存在的栏目名
下面编写一个类,检测某个字符串是否为栏目名
public class HostTable { private static String[] hosts = new String[]{ "hello", "123" }; public static boolean isHost(String str) { for (String s : hosts) { if (str.equals(s)) { return true; } } return false; } }
下面实现一个过滤器,具体过程见注释
public class HostFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException {} public void destroy() {} public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterchain) throws IOException, ServletException { HttpServletRequest httpreq = (HttpServletRequest) request; HttpServletResponse httpres = (HttpServletResponse) response; // 获取当前项目的appName String appName = httpreq.getContextPath(); // 获取请求的URL StringBuffer requestUrl = httpreq.getRequestURL(); System.out.println("过滤器,请求的地址:" + requestUrl); // 获取参数 int pos = requestUrl.lastIndexOf(appName + "/"); if (pos >= requestUrl.length() || pos < 0) { httpres.sendRedirect("/index.jsp"); return; } else { String parmar = requestUrl.substring(pos+appName.length()+1, requestUrl.length()); // 检测参数是否是允许的访问地址 if (HostTable.isHost(parmar)) { String url = "servlet/HostTestServlet?id="+parmar; System.out.println("过滤器,转到的地址:" + url); // 页面转向,浏览器地址栏能看到新的URL地址 //httpres.sendRedirect(url); // 页面转发,地址栏看不到新地址 request.getRequestDispatcher(url).forward(request,response); return; } else { System.out.println("参数parmar不在允许访问的地址列表中"); filterchain.doFilter(httpreq,httpres); } } } }
将Servlet和Filter注册到web.xml文件
在web.xml文件的<web-app></web-app>之间加入以下内容
<filter> <filter-name>HostFilter</filter-name> <filter-class>lmsk.sy.hostFilter.HostFilter</filter-class> </filter> <filter-mapping> <filter-name>HostFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>HostTestServlet</servlet-name> <servlet-class>lmsk.sy.hostFilter.HostTestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HostTestServlet</servlet-name> <url-pattern>/servlet/HostTestServlet</url-pattern> </servlet-mapping>
测试
发布到Tomcat,
在浏览器访问http://localhost:8080/HostTest/hello,页面上输出了hello
在浏览器访问http://localhost:8080/HostTest/123,页面输出了123
同时,控制台输出了调试信息
过滤器,请求的地址:http://localhost:8080/HostTest/hello
过滤器,转到的地址:servlet/HostTestServlet?id=hello
过滤器,请求的地址:http://localhost:8080/HostTest/123
过滤器,转到的地址:servlet/HostTestServlet?id=123
尝试访问某个未注册的值,出现404错误,同时控制台输出
过滤器,请求的地址:http://localhost:8080/HostTest/aaa
参数parmar不在允许访问的地址列表中
也可以修改过滤器代码,使出现该错误时,转到一个自定义的页面上。
通过重写HostTable.isHost(),让它根据数据库内容检测,可以实现栏目的动态地址访问。
补充:
1、应用到Struts2中时,发现不能正常转发.action请求。
原因是默认情况下,struts2的核心过滤器会过滤request请求,不会过滤forward请求。
解决方法是在web.xml文件配置核心过滤器的参数
<!-- 配置核心拦截器 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>
加入了<dispatcher>REQUEST</dispatcher>和<dispatcher>FORWARD</dispatcher>。