Skip to content

Commit 4d6a921

Browse files
SRIRAM9487rstoyanchev
authored andcommitted
Add HTTP method support to MappedInterceptor
This enhancement enables finer control over interceptor application based on HTTP methods, aligning with modern Spring 7.x practices. - Extend MappedInterceptor with include/exclude HTTP methods - Add constructors for interceptor implementations - Update InterceptorRegistration with fluent methods - Keep existing constructors and methods for compatibility - Update matches() to check HTTP method conditions See gh-35273 Signed-off-by: SRIRAM9487 <[email protected]>
1 parent 15d3692 commit 4d6a921

File tree

3 files changed

+271
-29
lines changed

3 files changed

+271
-29
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)