java doFilter 调用两次,预期行为?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16646819/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
doFilter called twice, intended behaviour?
提问by user1348661
I'm working through the Java EE servlet tutorial and tried the mood example. I noticed the doFilter is getting called twice, once the servlet call is in the chain and the second time it isnt.
我正在学习 Java EE servlet 教程并尝试了心情示例。我注意到 doFilter 被调用了两次,一次是 servlet 调用在链中,第二次不是。
I added some printlns in the TimeOfDayFilter.java and in the MoodServlet.java to show this.
我在 TimeOfDayFilter.java 和 MoodServlet.java 中添加了一些 println 来显示这一点。
TimeOfDayFilter.java:
TimeOfDayFilter.java:
...
System.out.println("TimeOfDay before"); //added
chain.doFilter(req, res);
System.out.println("TimeOfDay after"); //added
...
MoodServlet.java:
MoodServlet.java:
...
response.setContentType("text/html;charset=UTF-8");
System.out.println("MoodServlet"); //added
PrintWriter out = response.getWriter();
...
The result from the glassfish server (3.1) window when calling the servlet is the following:
调用 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
Is this intended behaviour? If so, what is the reason for the extra call?
这是预期的行为吗?如果是这样,额外呼叫的原因是什么?
回答by Rajarshee Mitra
chain.doFilter(request,response);
This will pass the control to the servlet the filter is associated with. But after the corresponding servlet is executed, the control comes back at the end of the above line and all the lines thereafter in the current doFilter() is executed.
这会将控制权传递给与过滤器关联的 servlet。但是在执行相应的 servlet 后,控制返回到上述行的末尾,并执行当前 doFilter() 中此后的所有行。
If you want to pass the control permanently to the servlet and not letting it return to the filter, just add a
如果您想将控件永久传递给 servlet 并且不让它返回到过滤器,只需添加一个
return;
at the end of chain.doFilter(request,response) line in the current filter.
在当前过滤器中的 chain.doFilter(request,response) 行的末尾。
回答by sebasbad
The Filter.doFilter
method is called once per request. You can execute some code before other filters in the chain are called and also afterwards (in the order specified in the filter chain, as per the web.xml filter-mapping
order),
something like the following example:
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);
}
}
If your filter is configured to dispatch REQUEST
and FORWARD
requests, then the MyFilter.doFilter
method will be called once for the original request and once if the request has been forwarded:
如果您的过滤器配置为分派REQUEST
和FORWARD
请求,则该MyFilter.doFilter
方法将为原始请求调用一次,如果请求已转发,则将调用一次:
Configure filter mapping using web.xml
file:
使用web.xml
文件配置过滤器映射:
...
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
...
Configure filter mapping using @WebFilterannotation:
使用@WebFilter注释配置过滤器映射:
@WebFilter(urlPatterns = "/*", dispatcherTypes = {
DispatcherType.REQUEST, DispatcherType.FORWARD
}) public MyFilter implements Filter {
...
}
To be able to check if the request has been forwarded, you can use the request attribute described here: How to know when the request is forwarded in a RequestWrapper object
为了能够检查请求是否已转发,您可以使用此处描述的请求属性:如何知道何时在 RequestWrapper 对象中转发请求
For details about filters see: https://docs.oracle.com/cd/B32110_01/web.1013/b28959/filters.htm
有关过滤器的详细信息,请参阅:https: //docs.oracle.com/cd/B32110_01/web.1013/b28959/filters.htm
回答by Mohan Pinapatruni
I solved the same issue after removing @Component in CustomFilter class.
在 CustomFilter 类中删除 @Component 后,我解决了同样的问题。
回答by Jonatas
As Mohan has stated, @Component will make your filter be called twice if you have it already registered in you Application class, like this:
正如 Mohan 所说,如果您已经在 Application 类中注册了过滤器,@Component 将使您的过滤器被调用两次,如下所示:
resources.add(new MyFilter());
If that's the case you have to choose between annotating it or registering it. But this is only valid for JAX-RS applications that use Spring. Not the topic of this question.
如果是这种情况,您必须在注释它或注册它之间做出选择。但这仅对使用 Spring 的 JAX-RS 应用程序有效。不是这个问题的主题。
回答by SilverFox
The reason why the filter is called twice are the images used in the response creation, as for instance
过滤器被调用两次的原因是响应创建中使用的图像,例如
out.println("<img src=\"resources/images/duke.snooze.gif\" alt=\"Duke sleeping\"/><br/>");
Please see the log output
请查看日志输出
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
src in img tag is nothing else than the second request for server to take care of. Please notice the url pattern used in the @WebFilter
img 标签中的 src 只不过是服务器要处理的第二个请求。请注意@WebFilter 中使用的 url 模式
@WebFilter(filterName = "TimeOfDayFilter",
urlPatterns = {"/*"},
initParams = {
@WebInitParam(name = "mood", value = "awake")})
It will intercept all the requests coming into mood application. As an exercise simply try to remove images from the response or change url pattern to intercept only requests ending up in MoodServlet
它将拦截所有进入心情应用程序的请求。作为练习,只需尝试从响应中删除图像或更改 url 模式以仅拦截以 MoodServlet 结尾的请求
@WebFilter(filterName = "TimeOfDayFilter",
urlPatterns = {"/report"},
initParams = {
@WebInitParam(name = "mood", value = "awake")})
Both will result in one call of doFilter as you originally expected
两者都会像您最初预期的那样调用一次 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
回答by Zeeshan Bilal
Yes it is, Filter is executed twice in the life cycle, first calls when request from client come to servlet and second time when response is served to the client after execution of servlet.
是的,过滤器在生命周期中执行了两次,第一次在来自客户端的请求来到 servlet 时调用,第二次在 servlet 执行后向客户端提供响应时调用。
The order of execution is somehow looks like this.
执行顺序不知何故看起来像这样。
回答by juan_carlos_yl
As @BalusC said, in my case the browser was requesting the favicon.ico by default, so there was an error in the browser's console
正如@BalusC 所说,在我的情况下,浏览器默认请求 favicon.ico,因此浏览器的控制台中出现错误
Failed to load resource: the server responded with a status of 404 (Not Found)
So I followed this answerand the error dissapeared but then I got the filter to get called twice.
所以我遵循了这个答案,错误消失了,但后来我让过滤器被调用了两次。
MY SOLUTION
I replaced
我更换的解决方案
<link rel="shortcut icon" href="#" />
For this:
为了这:
<link rel="shortcut icon" href="../assets/img/valid_icon.png" />
回答by neversleep
I came across the same issue when the doFilter
is called twice (or multiple times). The problem was that the filter handles every request including css, js, image and all other files while I expected one request for each page, so I solved the issue by adding the following code:
当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
Hope this will help people like me who googled this question .
希望这会帮助像我这样在谷歌上搜索这个问题的人。