27
27
28
28
import org .springframework .web .context .request .async .WebAsyncManager ;
29
29
import org .springframework .web .context .request .async .WebAsyncUtils ;
30
+ import org .springframework .web .util .WebUtils ;
30
31
31
32
/**
32
- * Filter base class that guarantees to be just executed once per request,
33
- * on any servlet container. It provides a {@link #doFilterInternal}
33
+ * Filter base class that aims to guarantee a single execution per request
34
+ * dispatch, on any servlet container. It provides a {@link #doFilterInternal}
34
35
* method with HttpServletRequest and HttpServletResponse arguments.
35
36
*
36
- * <p>In an async scenario a filter may be invoked again in additional threads
37
- * as part of an {@linkplain javax.servlet.DispatcherType.ASYNC ASYNC} dispatch.
38
- * Sub-classes may decide whether to be invoked once per request or once per
39
- * request thread for as long as the same request is being processed.
40
- * See {@link #shouldNotFilterAsyncDispatch()}.
37
+ * <p>As of Servlet 3.0, a filter may be invoked as part of a
38
+ * {@link javax.servlet.DispatcherType.REQUEST REQUEST} or
39
+ * {@link javax.servlet.DispatcherType.ASYNC ASYNC} dispatches that occur in
40
+ * separate threads. A filter can be configured in {@code web.xml} whether it
41
+ * should be involved in async dispatches. However, in some cases servlet
42
+ * containers assume different default configuration. Therefore sub-classes can
43
+ * override the method {@link #shouldNotFilterAsyncDispatch()} to declare
44
+ * statically if they shouuld indeed be invoked, <em>once</em>, during both types
45
+ * of dispatches in order to provide thread initialization, logging, security,
46
+ * and so on. This mechanism complements and does not replace the need to
47
+ * configure a filter in {@code web.xml} with dispatcher types.
41
48
*
42
- * <p>The {@link #getAlreadyFilteredAttributeName} method determines how
43
- * to identify that a request is already filtered. The default implementation
44
- * is based on the configured name of the concrete filter instance.
49
+ * <p>Sub-classes may use {@link #isAsyncDispatch(HttpServletRequest)} to
50
+ * determine when a filter is invoked as part of an async dispatch, and
51
+ * use {@link #isAsyncStarted(HttpServletRequest)} to determine when the
52
+ * request has been placed in async mode and therefore the current dispatch
53
+ * won't be the last one.
54
+ *
55
+ * <p>Yet another dispatch type that also occurs in its own thread is
56
+ * {@link javax.servlet.DispatcherType.ERROR ERROR}. Sub-classes can override
57
+ * {@link #shouldNotFilterErrorDispatch()} if they wish to declare statically
58
+ * if they should be invoked <em>once</em> during error dispatches.
59
+ *
60
+ * <p>The {@link #getAlreadyFilteredAttributeName} method determines how to
61
+ * identify that a request is already filtered. The default implementation is
62
+ * based on the configured name of the concrete filter instance.
45
63
*
46
64
* @author Juergen Hoeller
47
65
* @author Rossen Stoyanchev
@@ -74,13 +92,10 @@ public final void doFilter(ServletRequest request, ServletResponse response, Fil
74
92
HttpServletRequest httpRequest = (HttpServletRequest ) request ;
75
93
HttpServletResponse httpResponse = (HttpServletResponse ) response ;
76
94
77
- WebAsyncManager asyncManager = WebAsyncUtils .getAsyncManager (request );
78
- boolean processAsyncDispatch = asyncManager .hasConcurrentResult () && !shouldNotFilterAsyncDispatch ();
79
-
80
95
String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName ();
81
96
boolean hasAlreadyFilteredAttribute = request .getAttribute (alreadyFilteredAttributeName ) != null ;
82
97
83
- if (( hasAlreadyFilteredAttribute && (! processAsyncDispatch ) ) || shouldNotFilter (httpRequest )) {
98
+ if (hasAlreadyFilteredAttribute || skipDispatch ( httpRequest ) || shouldNotFilter (httpRequest )) {
84
99
85
100
// Proceed without invoking this filter...
86
101
filterChain .doFilter (request , response );
@@ -92,14 +107,46 @@ public final void doFilter(ServletRequest request, ServletResponse response, Fil
92
107
doFilterInternal (httpRequest , httpResponse , filterChain );
93
108
}
94
109
finally {
95
- if (!asyncManager .isConcurrentHandlingStarted ()) {
96
- // Remove the "already filtered" request attribute for this request.
97
- request .removeAttribute (alreadyFilteredAttributeName );
98
- }
110
+ // Remove the "already filtered" request attribute for this request.
111
+ request .removeAttribute (alreadyFilteredAttributeName );
99
112
}
100
113
}
101
114
}
102
115
116
+ private boolean skipDispatch (HttpServletRequest request ) {
117
+ if (isAsyncDispatch (request ) && shouldNotFilterAsyncDispatch ()) {
118
+ return true ;
119
+ }
120
+ if ((request .getAttribute (WebUtils .ERROR_REQUEST_URI_ATTRIBUTE ) != null ) && shouldNotFilterErrorDispatch ()) {
121
+ return true ;
122
+ }
123
+ return false ;
124
+ }
125
+
126
+ /**
127
+ * The dispatcher type {@code javax.servlet.DispatcherType.ASYNC} introduced
128
+ * in Servlet 3.0 means a filter can be invoked in more than one thread over
129
+ * the course of a single request. This method returns {@code true} if the
130
+ * filter is currently executing within an asynchronous dispatch.
131
+ *
132
+ * @param request the current request
133
+ * @see WebAsyncManager#hasConcurrentResult()
134
+ */
135
+ protected boolean isAsyncDispatch (HttpServletRequest request ) {
136
+ return WebAsyncUtils .getAsyncManager (request ).hasConcurrentResult ();
137
+ }
138
+
139
+ /**
140
+ * Whether request processing is in asynchronous mode meaning that the
141
+ * response will not be committed after the current thread is exited.
142
+ *
143
+ * @param request the current request
144
+ * @see WebAsyncManager#isConcurrentHandlingStarted()
145
+ */
146
+ protected boolean isAsyncStarted (HttpServletRequest request ) {
147
+ return WebAsyncUtils .getAsyncManager (request ).isConcurrentHandlingStarted ();
148
+ }
149
+
103
150
/**
104
151
* Return the name of the request attribute that identifies that a request
105
152
* is already filtered.
@@ -130,18 +177,18 @@ protected boolean shouldNotFilter(HttpServletRequest request) throws ServletExce
130
177
}
131
178
132
179
/**
133
- * Whether to filter async dispatches, which occur in a different thread.
134
180
* The dispatcher type {@code javax.servlet.DispatcherType.ASYNC} introduced
135
- * in Servlet 3.0 means a filter can be invoked in more than one thread (and
136
- * exited) over the course of a single request. Some filters only need to
137
- * filter the initial thread (e.g. request wrapping) while others may need
181
+ * in Servlet 3.0 means a filter can be invoked in more than one thread
182
+ * over the course of a single request. Some filters only need to filter
183
+ * the initial thread (e.g. request wrapping) while others may need
138
184
* to be invoked at least once in each additional thread for example for
139
185
* setting up thread locals or to perform final processing at the very end.
140
186
* <p>Note that although a filter can be mapped to handle specific dispatcher
141
187
* types via {@code web.xml} or in Java through the {@code ServletContext},
142
188
* servlet containers may enforce different defaults with regards to
143
189
* dispatcher types. This flag enforces the design intent of the filter.
144
- * <p>The default setting is "true", which means the filter will not be
190
+ *
191
+ * <p>The default return value is "true", which means the filter will not be
145
192
* invoked during subsequent async dispatches. If "false", the filter will
146
193
* be invoked during async dispatches with the same guarantees of being
147
194
* invoked only once during a request within a single thread.
@@ -150,6 +197,16 @@ protected boolean shouldNotFilterAsyncDispatch() {
150
197
return true ;
151
198
}
152
199
200
+ /**
201
+ * Whether to filter error dispatches such as when the servlet container
202
+ * processes and error mapped in {@code web.xml}. The default return value
203
+ * is "true", which means the filter will not be invoked in case of an error
204
+ * dispatch.
205
+ */
206
+ protected boolean shouldNotFilterErrorDispatch () {
207
+ return true ;
208
+ }
209
+
153
210
/**
154
211
* Same contract as for <code>doFilter</code>, but guaranteed to be
155
212
* just invoked once per request within a single request thread.
0 commit comments