13

我正在学习 Java EE servlet 教程并尝试了心情示例。我注意到 doFilter 被调用了两次,一次是 servlet 调用在链中,第二次不是。

我在 TimeOfDayFilter.java 和 MoodServlet.java 中添加了一些 println 来显示这一点。

TimeOfDayFilter.java:

    ...
    System.out.println("TimeOfDay before"); //added
    chain.doFilter(req, res);
    System.out.println("TimeOfDay after"); //added
    ...

MoodServlet.java:

    ...
    response.setContentType("text/html;charset=UTF-8");

    System.out.println("MoodServlet"); //added

    PrintWriter out = response.getWriter();
    ...

调用 servlet 时 glassfish 服务器 (3.1) 窗口的结果如下:

    INFO: mood was successfully deployed in 406 milliseconds.
    INFO: TimeOfDay before
    INFO: MoodServlet
    INFO: TimeOfDay after
    INFO: TimeOfDay before
    INFO: TimeOfDay after

这是预期的行为吗?如果是这样,额外调用的原因是什么?

4

8 回答 8

4
chain.doFilter(request,response);

这会将控制权传递给与过滤器关联的 servlet。但是在执行了相应的servlet之后,控制又回到了上述行的末尾,并且执行了当前doFilter()之后的所有行。

如果您想将控件永久传递给 servlet 并且不让它返回到过滤器,只需添加一个

return;

在当前过滤器中 chain.doFilter(request,response) 行的末尾。

于 2015-04-02T10:46:44.227 回答
4

每个请求调用一次该Filter.doFilter方法。您可以在调用链中的其他过滤器之前和之后执行一些代码(按照过滤器链中指定的顺序,按照web.xml filter-mapping顺序),类似于以下示例:

public MyFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response,
           FilterChain chain) 
           throws IOException, ServletException
    {
        codeToExecuteBeforeOtherFiltersInTheChain(request, response);

        chain.doFilter(request, response);

        codeToExecuteAfterOtherFiltersInTheChain(request, response);

    }
}

如果您的过滤器配置为分派REQUESTFORWARD请求,则该MyFilter.doFilter方法将对原始请求调用一次,如果请求已转发,则将调用一次:

web.xml使用文件配置过滤器映射:

...
<filter-mapping>
      <filter-name>MyFilter</filter-name>
      <url-pattern>/*</url-pattern>
      <dispatcher>REQUEST</dispatcher>
      <dispatcher>FORWARD</dispatcher>
</filter-mapping>
...

使用@WebFilter注解配置过滤器映射:

@WebFilter(urlPatterns = "/*", dispatcherTypes = {
    DispatcherType.REQUEST, DispatcherType.FORWARD
}) public MyFilter implements Filter {
    ...
}

为了能够检查请求是否已被转发,您可以使用此处描述的请求属性:如何知道请求何时在 RequestWrapper 对象中被转发

有关过滤器的详细信息,请参阅:https ://docs.oracle.com/cd/B32110_01/web.1013/b28959/filters.htm

于 2018-06-29T09:11:05.947 回答
2

I solved the same issue after removing @Component in CustomFilter class.

于 2018-08-28T02:24:05.783 回答
1

是的,过滤器在生命周期中执行了两次,第一次是在客户端的请求到达 servlet 时调用,第二次是在 servlet 执行后向客户端提供响应时调用。

执行顺序不知何故是这样的。

过滤器生命周期

于 2013-05-20T09:58:33.033 回答
1

过滤器被调用两次的原因是响应创建中使用的图像,例如

out.println("<img src=\"resources/images/duke.snooze.gif\" alt=\"Duke sleeping\"/><br/>");

请查看日志输出

2016-01-16T11:25:34.894+0100|Info: TimeOfDay doFilter method before sending to chain

2016-01-16T11:25:34.895+0100|Info: MoodServlet get method called

2016-01-16T11:25:34.895+0100|Info: TimeOfDay doFilter method after sending to chain

2016-01-16T11:25:34.942+0100|Info: TimeOfDay doFilter method before sending to chain

2016-01-16T11:25:34.942+0100|Info: TimeOfDay doFilter method after sending to chain

img 标签中的 src 只不过是服务器要处理的第二个请求。请注意@WebFilter 中使用的 url 模式

@WebFilter(filterName = "TimeOfDayFilter",
urlPatterns = {"/*"},
initParams = {
    @WebInitParam(name = "mood", value = "awake")})

它将拦截所有进入心情应用程序的请求。作为练习,只需尝试从响应中删除图像或更改 url 模式以仅拦截以 MoodServlet 结尾的请求

@WebFilter(filterName = "TimeOfDayFilter",
urlPatterns = {"/report"},
initParams = {
    @WebInitParam(name = "mood", value = "awake")})

正如您最初预期的那样,两者都将导致一次 doFilter 调用

2016-01-16T11:28:53.485+0100|Info: TimeOfDay doFilter method before sending to chain

2016-01-16T11:28:53.486+0100|Info: MoodServlet get method called

2016-01-16T11:28:53.487+0100|Info: TimeOfDay doFilter method after sending to chain
于 2016-01-16T10:20:23.447 回答
1

正如 Mohan 所说,如果您已经在 Application 类中注册了 @Component 过滤器,它将使您的过滤器被调用两次,如下所示:

resources.add(new MyFilter());

如果是这种情况,您必须在注释或注册之间做出选择。但这仅对使用 Spring 的 JAX-RS 应用程序有效。不是这个问题的主题。

于 2019-06-16T16:25:32.740 回答
0

doFilter当被调用两次(或多次)时,我遇到了同样的问题。问题是过滤器处理每个请求,包括 css、js、图像和所有其他文件,而我希望每个页面都有一个请求,所以我通过添加以下代码解决了这个问题:

@WebFilter(filterName = "MyCustomFilter")
public class MyCustomFilter implements Filter {

  public void doFilter(ServletRequest request,ServletResponse response,
          FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    String accept = httpRequest.getHeader("accept");

    // Since the filter handles every request
    // we have to ensure that the request is asking for text/html
    if (accept == null || !accept.toLowerCase().startsWith("text/html")) {
      chain.doFilter(request, response);
      return;
    }

    // your code goes here

希望这会帮助像我这样用谷歌搜索这个问题的人。

于 2019-05-02T04:55:13.560 回答
0

正如@BalusC 所说,在我的情况下,浏览器默认请求 favicon.ico,所以浏览器的控制台出现错误

Failed to load resource: the server responded with a status of 404 (Not Found)

所以我遵循了这个答案 ,错误消失了,但后来我让过滤器被调用了两次。

我的解决方案

我替换了

<link rel="shortcut icon" href="#" />

为了这:

<link rel="shortcut icon" href="../assets/img/valid_icon.png" />
于 2020-03-06T05:03:59.083 回答