【问题标题】:Why does my Servlet Filter run twice?为什么我的 Servlet 过滤器运行两次?
【发布时间】:2017-01-13 06:13:34
【问题描述】:

我有一个简单的 Servlet 过滤器:

package net.twentyonesolutions;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;

public class TestFilter implements Filter {

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println(TestFilter.class.getSimpleName()       // line 12
                + " > "
                + ((HttpServletRequest)servletRequest).getRequestURL()
                + ": IP "+ servletRequest.getRemoteAddr()
                + "; at "
                + new Date().toString());

        // Pass request back down the filter chain
        filterChain.doFilter(servletRequest, servletResponse);    // line 20
    }

    public void init(FilterConfig filterConfig) throws ServletException {}

    public void destroy() {}
}

我像这样将它添加到 web.xml 中:

<filter>
    <filter-name>TestFilter</filter-name>
    <filter-class>net.twentyonesolutions.TestFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>TestFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

我在带有嵌入式 Jetty 的 IntelliJ IDEA 中以调试模式运行它。我希望第 12 行的输出被写入一次,但它被写入两次,并且两次都是在调用 Servlet 之前。

为什么要调用两次?它应该被调用两次吗?如果是这样,我如何区分第一次和第二次,因为我希望我的代码只运行一次。

我发现了几个关于这个问题的老问题,但没有一个真正的解决方案或解释。

您可以在下面的屏幕截图中看到第一次调用和第二次调用之间堆栈跟踪的差异:

更新

当我将过滤器部署到常规(非嵌入式)码头(与嵌入式码头相同的版本,9.2.18.v20160721)或 Tomcat 时——doFilter() 正如预期的那样,每个请求仅执行一次,所以问题是在 IntelliJ IDEA 中进行调试或使用嵌入式 Jetty。

更新嵌入代码:

    WebAppContext webapp = new WebAppContext();
    webapp.setContextPath("/");
    webapp.setResourceBase(webRoot);
    webapp.setDefaultsDescriptor(webRoot + "/WEB-INF/web.xml");     

    Server server = new Server();

    ServerConnector http = new ServerConnector(server);
    http.setPort(port);             
    if (!host.equals(""))
        http.setHost(host);

    server.setConnectors(new ServerConnector[] { http });
    server.setHandler(webapp);      
    server.start();
    server.join();

更新 filterChain.toString()

我在调用 filterChain.doFilter() 之前添加了 filterChain.toString() 的打印,它清楚地显示了差异。

filterChain 中的第一个对象(在第一次调用 doFilter() 之前)是我的过滤器。看两行打印:

TestFilter > fdvnqp1k0ut37fenee569vshqf http://localhost:8080/index.cfm: IP 0:0:0:0:0:0:0:1; at Tue Sep 06 09:59:09 PDT 2016
    ... filterChain: TestFilter->CFMLServlet@cdad6a3==lucee.debug.loader.servlet.CFMLServlet,1,true
TestFilter > fdvnqp1k0ut37fenee569vshqf http://localhost:8080/index.cfm: IP 0:0:0:0:0:0:0:1; at Tue Sep 06 09:59:09 PDT 2016
    ... filterChain: CFMLServlet@cdad6a3==lucee.debug.loader.servlet.CFMLServlet,1,true

我不确定规范,但我希望在第一次调用 TestFilter.doFilter() 之前将我的 TestFilter 从链中删除,不是吗?

【问题讨论】:

  • 重定向?所以你会看到两个请求。
  • 重定向从何而来?如果是这样,为什么 Servlet 只被调用一次?
  • 为了进一步确认,我尝试了不同的&lt;dispatcher&gt; 属性。只有REQUEST 会导致过滤器运行,这是我所期望的,因为我正在使用简单的http GET 请求访问服务器。所有其他类型,即 FORWARD、INCLUDE 和 ERROR 根本不会触发过滤器。
  • 如果您对这个问题投了反对票,请解释原因。至少它写得很清楚IMO。
  • 你能展示你的嵌入代码吗?

标签: servlets jakarta-ee intellij-idea servlet-filters embedded-jetty


【解决方案1】:

我认为问题在于您将 WEB-INF/web.xml 设置为默认描述符。所以 web.xml 运行了两次——一次设置默认值,一次作为发现的 WEB-INF/web.xml——所以你得到了两个过滤器实例。

不要设置默认描述符(如果您想知道默认值,也可以将其设置为 null - 但是您将没有默认 servlet)。

【讨论】:

  • 所以你的意思是打电话给webapp.setDescriptor()而不是webapp.setDefaultDecriptor()
  • 谢谢你,格雷格!感谢您创建 Jetty 项目 :)
猜你喜欢
  • 2010-10-23
  • 2012-08-14
  • 2011-04-09
  • 1970-01-01
  • 1970-01-01
  • 2013-07-27
  • 1970-01-01
  • 2016-06-16
  • 2022-01-11
相关资源
最近更新 更多