如何在Java中使用servlet过滤器来更改传入的servlet请求URL?

204
如何使用Servlet过滤器将传入的Servlet请求URL从
http://nm-java.appspot.com/Check_License/Dir_My_App/Dir_ABC/My_Obj_123
更改为
http://nm-java.appspot.com/Check_License?Contact_Id=My_Obj_123

更新:根据BalusC以下步骤,我编写了以下代码:
public class UrlRewriteFilter implements Filter {

    @Override
    public void init(FilterConfig config) throws ServletException {
        //
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        String requestURI = request.getRequestURI();

        if (requestURI.startsWith("/Check_License/Dir_My_App/")) {
            String toReplace = requestURI.substring(requestURI.indexOf("/Dir_My_App"), requestURI.lastIndexOf("/") + 1);
            String newURI = requestURI.replace(toReplace, "?Contact_Id=");
            req.getRequestDispatcher(newURI).forward(req, res);
        } else {
            chain.doFilter(req, res);
        }
    }

    @Override
    public void destroy() {
        //
    }
}

相关的web.xml条目如下所示:

<filter>
    <filter-name>urlRewriteFilter</filter-name>
    <filter-class>com.example.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>urlRewriteFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

我尝试了服务器端和客户端的重定向,预期的结果都得到了。谢谢BalusC!


相关问题:http://stackoverflow.com/questions/2723829/how-to-catch-non-exist-requested-url-in-java-servlet - BalusC
你使用的Servlet规范版本是哪个?随着不同版本的变化,你如何转发请求也会有所改变。 - Romain Hippeau
请查看此帖子(http://stackoverflow.com/questions/2283237/most-useful-java-servlet-filter-out-there),它还有一个可以满足您需求的过滤器。 - Romain Hippeau
谢谢,我正在寻找有关URL重写和加密的示例。 - Aditya Yada
4个回答

295
  1. 实现javax.servlet.Filter
  2. doFilter()方法中,将传入的ServletRequest转换为HttpServletRequest
  3. 使用HttpServletRequest#getRequestURI()获取路径。
  4. 使用简单的java.lang.String方法,如substring(), split(), concat()等来提取感兴趣的部分并组合成新的路径。
  5. 要么使用ServletRequest#getRequestDispatcher(),然后使用RequestDispatcher#forward()将请求/响应转发到新的URL(服务器端重定向,在浏览器地址栏中不反映),要么将传入的ServletResponse转换为HttpServletResponse,然后使用HttpServletResponse#sendRedirect()将响应重定向到新的URL(客户端重定向,在浏览器地址栏中反映)。
  6. web.xml中将过滤器注册到/*/Check_License/*url-pattern上,具体取决于上下文路径,或者如果您已经在Servlet 3.0上,则改用@WebFilter注释。
不要忘记在代码中添加检查,以确定URL是否需要更改。如果不需要更改,则只需调用FilterChain#doFilter();否则,它将会在无限循环中调用自身。
或者,您也可以使用现有的第三方API来完成所有工作,例如Tuckey's UrlRewriteFilter,可以像使用Apache的mod_rewrite一样进行配置。

20
你卡在了哪个步骤上?我的答案几乎可以自己编写代码。你是否注意到蓝色的代码引用实际上是指向Javadocs的链接,其中详细描述了类或方法的行为?无论如何,你可以在这里找到好的JSP / Servlet教程,具体而言是关于过滤器的这个 - BalusC
2
我认为这是正确的,但如果过滤器是链中的第一个,并且执行了RequestDispatcher#forward(),则其余过滤器将不会被执行。那么,难道不是将其作为servlet来处理更好吗? - lucasvc
2
@datakey:只需重新排列顺序或在过滤器映射中添加<dispatcher>FORWARD</dispatcher>即可。 - BalusC
2
@BalusC,我们能否在不重定向或调用转发的情况下完成它? - Amandeep Jiddewar
1
这个解决方案是可行的,但如果我们有一系列的过滤器呢?据我所知,这个解决方案破坏了过滤器的逻辑 - 当有一系列的过滤器时,每个过滤器在处理请求之前和之后都必须做一些事情。但是这个解决方案却将请求转发到某个Servlet,这不是它的工作。 - Pavel_K
显示剩余6条评论

22

你可以使用现成的Url Rewrite Filter,并使用这样的规则:

<rule>
  <from>^/Check_License/Dir_My_App/Dir_ABC/My_Obj_([0-9]+)$</from>
  <to>/Check_License?Contact_Id=My_Obj_$1</to>
</rule>

查看示例以获取更多...示例。


您好,粘贴的链接已经失效了,出现了404错误。请求您更换其他来源。谢谢。 - KnockingHeads

10

一种基于BalusC的答案步骤的简单JSF Url美化过滤器。该过滤器将所有以/ui路径开头的请求(假设您将所有xhtml文件存储在那里)转发到相同的路径,但添加了xhtml后缀。

public class UrlPrettyfierFilter implements Filter {

    private static final String JSF_VIEW_ROOT_PATH = "/ui";

    private static final String JSF_VIEW_SUFFIX = ".xhtml";

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpServletRequest = ((HttpServletRequest) request);
        String requestURI = httpServletRequest.getRequestURI();
        //Only process the paths starting with /ui, so as other requests get unprocessed. 
        //You can register the filter itself for /ui/* only, too
        if (requestURI.startsWith(JSF_VIEW_ROOT_PATH) 
                && !requestURI.contains(JSF_VIEW_SUFFIX)) {
            request.getRequestDispatcher(requestURI.concat(JSF_VIEW_SUFFIX))
                .forward(request,response);
        } else {
            chain.doFilter(httpServletRequest, response);
        }
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {

    }

}

4

在我的情况下,我使用Spring框架,但由于某些原因,forward命令无法正常工作。因此,我采取了以下措施:

public class OldApiVersionFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        if (httpServletRequest.getRequestURI().contains("/api/v3/")) {
            HttpServletRequest modifiedRequest = new HttpServletRequestWrapper((httpServletRequest)) {
                @Override
                public String getRequestURI() {
                    return httpServletRequest.getRequestURI().replaceAll("/api/v3/", "/api/");
                }
            };
            chain.doFilter(modifiedRequest, response);
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void destroy() {}
}

请确保链接修改后的请求(modifiedRequest)


也许 "forrward" 没有起作用的原因是因为它应该是 "forward"。 - Pere
你可以编辑错别字并覆盖整个注释行! - Youans
这样做会改变你的回复意义,Youans,因为我不能确定它是你在回答时打错了字还是源代码中的错误。你本可以进行适当的编辑,因为只有你知道正确的情况,但你没有这样做。 顺便说一下,我也打错了一个字,但我无法更正。我想说的是“work”而不是“fork”... 哎呀! - Pere
@Pere 这不是代码的问题!不过我理解你的观点,但即使这是代码,也不像是由机器人编写的。 - Youans
这对我有用。非常感谢你! - R.Wedisa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接