Skip to content

Commit bd7617f

Browse files
committed
Add HTTP method-based filtering support to MappedInterceptor and InterceptorRegistration
- Extended MappedInterceptor to support include/exclude HTTP methods for request matching - Added corresponding constructors for HandlerInterceptor and WebRequestInterceptor - Updated InterceptorRegistration with fluent methods to configure HTTP method filters - Ensured backward compatibility by keeping existing constructors and methods intact - Improved matches() logic to combine URL pattern and HTTP method conditions This enhancement enables finer control over interceptor application based on HTTP methods, aligning with modern Spring 7.x practices.
1 parent 4bfc129 commit bd7617f

File tree

2 files changed

+173
-19
lines changed

2 files changed

+173
-19
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/InterceptorRegistration.java

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import org.jspecify.annotations.Nullable;
2424

25+
import org.springframework.http.HttpMethod;
2526
import org.springframework.util.AntPathMatcher;
2627
import org.springframework.util.Assert;
2728
import org.springframework.util.PathMatcher;
@@ -48,6 +49,10 @@ public class InterceptorRegistration {
4849

4950
private @Nullable List<String> excludePatterns;
5051

52+
private @Nullable List<HttpMethod> includeHttpMethods;
53+
54+
private @Nullable List<HttpMethod> excludeHttpMethods;
55+
5156
private @Nullable PathMatcher pathMatcher;
5257

5358
private int order = 0;
@@ -106,6 +111,46 @@ public InterceptorRegistration excludePathPatterns(List<String> patterns) {
106111
return this;
107112
}
108113

114+
/**
115+
* Add HTTP methods the interceptor should be included for.
116+
* <p>Only requests with these HTTP methods will be intercepted.
117+
* @since 7.0.x
118+
*/
119+
public InterceptorRegistration includeHttpMethods(HttpMethod... httpMethods) {
120+
return includeHttpMethods(Arrays.asList(httpMethods));
121+
}
122+
123+
/**
124+
* List-based variant of {@link #includeHttpMethods(HttpMethod...)}.
125+
* @since 7.0.x
126+
*/
127+
public InterceptorRegistration includeHttpMethods(List<HttpMethod> httpMethods) {
128+
this.includeHttpMethods = (this.includeHttpMethods != null ?
129+
this.includeHttpMethods : new ArrayList<>(httpMethods.size()));
130+
this.includeHttpMethods.addAll(httpMethods);
131+
return this;
132+
}
133+
134+
/**
135+
* Add HTTP methods the interceptor should be excluded from.
136+
* <p>Requests with these HTTP methods will be ignored by the interceptor.
137+
* @since 7.0.x
138+
*/
139+
public InterceptorRegistration excludeHttpMethods(HttpMethod... httpMethods){
140+
return this.excludeHttpMethods(Arrays.asList(httpMethods));
141+
}
142+
143+
/**
144+
* List-based variant of {@link #excludeHttpMethods(HttpMethod...)}.
145+
* @since 7.0.x
146+
*/
147+
public InterceptorRegistration excludeHttpMethods(List<HttpMethod> httpMethods){
148+
this.excludeHttpMethods = (this.excludeHttpMethods != null ?
149+
this.excludeHttpMethods : new ArrayList<>(httpMethods.size()));
150+
this.excludeHttpMethods.addAll(httpMethods);
151+
return this;
152+
}
153+
109154
/**
110155
* Configure the PathMatcher to use to match URL paths with against include
111156
* and exclude patterns.
@@ -143,19 +188,32 @@ protected int getOrder() {
143188
}
144189

145190
/**
146-
* Build the underlying interceptor. If URL patterns are provided, the returned
191+
* Build the underlying interceptor. If URL patterns or HTTP methods are provided, the returned
147192
* type is {@link MappedInterceptor}; otherwise {@link HandlerInterceptor}.
148193
*/
149194
@SuppressWarnings("removal")
150195
protected Object getInterceptor() {
151196

152-
if (this.includePatterns == null && this.excludePatterns == null) {
197+
if (this.includePatterns == null && this.excludePatterns == null && this.includeHttpMethods == null && this.excludeHttpMethods == null) {
153198
return this.interceptor;
154199
}
155200

201+
HttpMethod[] includeMethodsArray = (this.includeHttpMethods != null) ?
202+
this.includeHttpMethods.toArray(new HttpMethod[0]) : null;
203+
204+
HttpMethod[] excludeMethodsArray = (this.excludeHttpMethods != null) ?
205+
this.excludeHttpMethods.toArray(new HttpMethod[0]) : null;
206+
207+
String[] includePattersArray = StringUtils.toStringArray(this.includePatterns);
208+
209+
String[] excludePattersArray = StringUtils.toStringArray(this.excludePatterns);
210+
211+
156212
MappedInterceptor mappedInterceptor = new MappedInterceptor(
157-
StringUtils.toStringArray(this.includePatterns),
158-
StringUtils.toStringArray(this.excludePatterns),
213+
includePattersArray,
214+
excludePattersArray,
215+
includeMethodsArray,
216+
excludeMethodsArray,
159217
this.interceptor);
160218

161219
if (this.pathMatcher != null) {

spring-webmvc/src/main/java/org/springframework/web/servlet/handler/MappedInterceptor.java

Lines changed: 111 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import jakarta.servlet.http.HttpServletResponse;
2323
import org.jspecify.annotations.Nullable;
2424

25+
import org.springframework.http.HttpMethod;
2526
import org.springframework.http.server.PathContainer;
2627
import org.springframework.util.AntPathMatcher;
2728
import org.springframework.util.ObjectUtils;
@@ -67,6 +68,10 @@ public final class MappedInterceptor implements HandlerInterceptor {
6768

6869
private final PatternAdapter @Nullable [] excludePatterns;
6970

71+
private final MethodAdapter @Nullable [] includeHttpMethods;
72+
73+
private final MethodAdapter @Nullable [] excludeHttpMethods;
74+
7075
private PathMatcher pathMatcher = defaultPathMatcher;
7176

7277
private final HandlerInterceptor interceptor;
@@ -78,58 +83,79 @@ public final class MappedInterceptor implements HandlerInterceptor {
7883
* @param includePatterns patterns to which requests must match, or null to
7984
* match all paths
8085
* @param excludePatterns patterns to which requests must not match
86+
* @param includeHttpMethods http methods to which request must match, or null to match all paths
87+
* @param excludeHttpMethods http methods to which request must not match
8188
* @param interceptor the target interceptor
8289
* @param parser a parser to use to pre-parse patterns into {@link PathPattern};
8390
* when not provided, {@link PathPatternParser#defaultInstance} is used.
8491
* @since 5.3
8592
*/
86-
public MappedInterceptor(String @Nullable [] includePatterns, String @Nullable [] excludePatterns,
93+
public MappedInterceptor(String @Nullable [] includePatterns, String @Nullable [] excludePatterns, HttpMethod @Nullable [] includeHttpMethods, HttpMethod @Nullable [] excludeHttpMethods,
8794
HandlerInterceptor interceptor, @Nullable PathPatternParser parser) {
8895

8996
this.includePatterns = PatternAdapter.initPatterns(includePatterns, parser);
9097
this.excludePatterns = PatternAdapter.initPatterns(excludePatterns, parser);
98+
this.includeHttpMethods = MethodAdapter.initHttpMethods(includeHttpMethods);
99+
this.excludeHttpMethods = MethodAdapter.initHttpMethods(excludeHttpMethods);
91100
this.interceptor = interceptor;
92101
}
93102

94103

95104
/**
96105
* Variant of
97-
* {@link #MappedInterceptor(String[], String[], HandlerInterceptor, PathPatternParser)}
106+
* {@link #MappedInterceptor(String[], String[], HttpMethod[], HttpMethod[], HandlerInterceptor, PathPatternParser)}
98107
* with include patterns only.
99108
*/
100109
public MappedInterceptor(String @Nullable [] includePatterns, HandlerInterceptor interceptor) {
101-
this(includePatterns, null, interceptor);
110+
this(includePatterns, null, null, null, interceptor);
102111
}
103112

104113
/**
105114
* Variant of
106-
* {@link #MappedInterceptor(String[], String[], HandlerInterceptor, PathPatternParser)}
115+
* {@link #MappedInterceptor(String[], String[], HttpMethod[], HttpMethod[], HandlerInterceptor, PathPatternParser)}
116+
* with include methods only.
117+
*/
118+
public MappedInterceptor(HttpMethod @Nullable [] includeHttpMethods, HandlerInterceptor interceptor) {
119+
this(null, null, includeHttpMethods, null, interceptor);
120+
}
121+
122+
/**
123+
* Variant of
124+
* {@link #MappedInterceptor(String[], String[], HttpMethod[], HttpMethod[], HandlerInterceptor, PathPatternParser)}
107125
* without a provided parser.
108126
*/
109-
public MappedInterceptor(String @Nullable [] includePatterns, String @Nullable [] excludePatterns,
127+
public MappedInterceptor(String @Nullable [] includePatterns, String @Nullable [] excludePatterns, HttpMethod @Nullable [] includeHttpMethods, HttpMethod @Nullable [] excludeHttpMethods,
110128
HandlerInterceptor interceptor) {
111129

112-
this(includePatterns, excludePatterns, interceptor, null);
130+
this(includePatterns, excludePatterns,includeHttpMethods,excludeHttpMethods, interceptor, null);
113131
}
114132

115133
/**
116134
* Variant of
117-
* {@link #MappedInterceptor(String[], String[], HandlerInterceptor, PathPatternParser)}
135+
* {@link #MappedInterceptor(String[], String[], HttpMethod[], HttpMethod[], HandlerInterceptor, PathPatternParser)}
118136
* with a {@link WebRequestInterceptor} as the target.
119137
*/
120138
public MappedInterceptor(String @Nullable [] includePatterns, WebRequestInterceptor interceptor) {
121-
this(includePatterns, null, interceptor);
139+
this(includePatterns, null,null,null, interceptor);
140+
}
141+
/**
142+
* Variant of
143+
* {@link #MappedInterceptor(String[], String[], HttpMethod[], HttpMethod[], HandlerInterceptor, PathPatternParser)}
144+
* with a {@link WebRequestInterceptor} as the target.
145+
*/
146+
public MappedInterceptor(HttpMethod @Nullable [] includeHttpMethods, WebRequestInterceptor interceptor) {
147+
this(null, null,includeHttpMethods ,null, interceptor);
122148
}
123149

124150
/**
125151
* Variant of
126-
* {@link #MappedInterceptor(String[], String[], HandlerInterceptor, PathPatternParser)}
152+
* {@link #MappedInterceptor(String[], String[], HttpMethod[], HttpMethod[] , HandlerInterceptor, PathPatternParser)}
127153
* with a {@link WebRequestInterceptor} as the target.
128154
*/
129-
public MappedInterceptor(String @Nullable [] includePatterns, String @Nullable [] excludePatterns,
155+
public MappedInterceptor(String @Nullable [] includePatterns, String @Nullable [] excludePatterns, HttpMethod @Nullable [] includeHttpMethods, HttpMethod @Nullable [] excludeHttpMethods,
130156
WebRequestInterceptor interceptor) {
131157

132-
this(includePatterns, excludePatterns, new WebRequestHandlerInterceptorAdapter(interceptor));
158+
this(includePatterns, excludePatterns,includeHttpMethods,excludeHttpMethods, new WebRequestHandlerInterceptorAdapter(interceptor));
133159
}
134160

135161

@@ -202,6 +228,7 @@ public PathMatcher getPathMatcher() {
202228
*/
203229
public boolean matches(HttpServletRequest request) {
204230
Object path = ServletRequestPathUtils.getCachedPath(request);
231+
HttpMethod method = HttpMethod.valueOf(request.getMethod());
205232
if (this.pathMatcher != defaultPathMatcher) {
206233
path = path.toString();
207234
}
@@ -213,12 +240,45 @@ public boolean matches(HttpServletRequest request) {
213240
}
214241
}
215242
}
216-
if (ObjectUtils.isEmpty(this.includePatterns)) {
243+
if (!ObjectUtils.isEmpty(this.excludeHttpMethods)) {
244+
for (MethodAdapter adapter : this.excludeHttpMethods) {
245+
if (adapter.match(method)){
246+
return false;
247+
}
248+
}
249+
}
250+
if (ObjectUtils.isEmpty(this.includePatterns) && ObjectUtils.isEmpty(this.includeHttpMethods)) {
217251
return true;
218252
}
219-
for (PatternAdapter adapter : this.includePatterns) {
220-
if (adapter.match(path, isPathContainer, this.pathMatcher)) {
221-
return true;
253+
if (!ObjectUtils.isEmpty(this.includePatterns) && ObjectUtils.isEmpty(this.includeHttpMethods)) {
254+
for (PatternAdapter adapter : this.includePatterns) {
255+
if (adapter.match(path, isPathContainer, this.pathMatcher)) {
256+
return true;
257+
}
258+
}
259+
}
260+
if (!ObjectUtils.isEmpty(this.includeHttpMethods) && ObjectUtils.isEmpty(this.includePatterns)) {
261+
for (MethodAdapter adapter : this.includeHttpMethods) {
262+
if (adapter.match(method)) {
263+
return true;
264+
}
265+
}
266+
}
267+
if (!ObjectUtils.isEmpty(this.includePatterns) && !ObjectUtils.isEmpty(this.includeHttpMethods)) {
268+
boolean match = false;
269+
for (MethodAdapter methodAdapter : this.includeHttpMethods) {
270+
if (methodAdapter.match(method)) {
271+
match = true;
272+
break;
273+
}
274+
}
275+
if (!match) {
276+
return false;
277+
}
278+
for (PatternAdapter pathAdapter : this.includePatterns) {
279+
if (pathAdapter.match(path, isPathContainer, pathMatcher)) {
280+
return true;
281+
}
222282
}
223283
}
224284
return false;
@@ -305,4 +365,40 @@ public boolean match(Object path, boolean isPathContainer, PathMatcher pathMatch
305365
}
306366
}
307367

368+
/**
369+
* Adapts {@link HttpMethod} instances for internal matching purposes.
370+
*
371+
* <p>Encapsulates an {@link HttpMethod} and provides matching functionality.
372+
* Also provides a utility method to initialize arrays of {@code MethodAdapter}
373+
* instances from arrays of {@link HttpMethod}.</p>
374+
*
375+
* @since 7.0.x
376+
*/
377+
private static class MethodAdapter {
378+
379+
private final @Nullable HttpMethod httpMethod;
380+
381+
public MethodAdapter(@Nullable HttpMethod httpMethod) {
382+
this.httpMethod = httpMethod;
383+
}
384+
385+
public boolean match(HttpMethod method) {
386+
return this.httpMethod == method;
387+
}
388+
389+
public @Nullable HttpMethod getHttpMethod() {
390+
return this.httpMethod;
391+
}
392+
393+
private static MethodAdapter @Nullable [] initHttpMethods(HttpMethod @Nullable [] methods) {
394+
if (ObjectUtils.isEmpty(methods)) {
395+
return null;
396+
}
397+
return Arrays.stream(methods)
398+
.map(MethodAdapter::new)
399+
.toArray(MethodAdapter[]::new);
400+
}
401+
402+
}
403+
308404
}

0 commit comments

Comments
 (0)