Skip to content

Commit 776e28a

Browse files
committed
MockMvc supports filter initParams and DispatcherType's
Closes gh-27717, gh-31362
1 parent 0542fe5 commit 776e28a

File tree

6 files changed

+163
-53
lines changed

6 files changed

+163
-53
lines changed

spring-test/src/main/java/org/springframework/test/web/servlet/MockMvcBuilderSupport.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -55,7 +55,10 @@ protected final MockMvc createMockMvc(Filter[] filters, MockServletConfig servle
5555
List<ResultMatcher> globalResultMatchers, List<ResultHandler> globalResultHandlers,
5656
@Nullable List<DispatcherServletCustomizer> dispatcherServletCustomizers) {
5757

58-
MockMvc mockMvc = createMockMvc(filters, servletConfig, webAppContext, defaultRequestBuilder, globalResultMatchers, globalResultHandlers, dispatcherServletCustomizers);
58+
MockMvc mockMvc = createMockMvc(
59+
filters, servletConfig, webAppContext, defaultRequestBuilder,
60+
globalResultMatchers, globalResultHandlers, dispatcherServletCustomizers);
61+
5962
mockMvc.setDefaultResponseCharacterEncoding(defaultResponseCharacterEncoding);
6063
return mockMvc;
6164
}
@@ -75,7 +78,7 @@ protected final MockMvc createMockMvc(Filter[] filters, MockServletConfig servle
7578
dispatcherServlet.init(servletConfig);
7679
}
7780
catch (ServletException ex) {
78-
// should never happen..
81+
// should never happen...
7982
throw new MockMvcBuildException("Failed to initialize TestDispatcherServlet", ex);
8083
}
8184

spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,10 +18,14 @@
1818

1919
import java.nio.charset.Charset;
2020
import java.util.ArrayList;
21+
import java.util.EnumSet;
2122
import java.util.List;
23+
import java.util.Map;
2224

25+
import jakarta.servlet.DispatcherType;
2326
import jakarta.servlet.Filter;
2427
import jakarta.servlet.ServletContext;
28+
import jakarta.servlet.ServletException;
2529

2630
import org.springframework.lang.Nullable;
2731
import org.springframework.mock.web.MockServletConfig;
@@ -76,9 +80,9 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>
7680
@Override
7781
public final <T extends B> T addFilters(Filter... filters) {
7882
Assert.notNull(filters, "filters cannot be null");
79-
for (Filter f : filters) {
80-
Assert.notNull(f, "filters cannot contain null values");
81-
this.filters.add(f);
83+
for (Filter filter : filters) {
84+
Assert.notNull(filter, "filters cannot contain null values");
85+
this.filters.add(filter);
8286
}
8387
return self();
8488
}
@@ -88,12 +92,22 @@ public final <T extends B> T addFilter(Filter filter, String... urlPatterns) {
8892
Assert.notNull(filter, "filter cannot be null");
8993
Assert.notNull(urlPatterns, "urlPatterns cannot be null");
9094
if (urlPatterns.length > 0) {
91-
filter = new PatternMappingFilterProxy(filter, urlPatterns);
95+
filter = new MockMvcFilterDecorator(filter, urlPatterns);
9296
}
9397
this.filters.add(filter);
9498
return self();
9599
}
96100

101+
@Override
102+
public <T extends B> T addFilter(
103+
Filter filter, Map<String, String> initParams,
104+
EnumSet<DispatcherType> dispatcherTypes, String... urlPatterns) {
105+
106+
filter = new MockMvcFilterDecorator(filter, initParams, dispatcherTypes, urlPatterns);
107+
this.filters.add(filter);
108+
return self();
109+
}
110+
97111
@Override
98112
public final <T extends B> T defaultRequest(RequestBuilder requestBuilder) {
99113
this.defaultRequestBuilder = requestBuilder;
@@ -171,6 +185,16 @@ public final MockMvc build() {
171185
}
172186

173187
Filter[] filterArray = this.filters.toArray(new Filter[0]);
188+
for (Filter filter : filterArray) {
189+
if (filter instanceof MockMvcFilterDecorator filterDecorator) {
190+
try {
191+
filterDecorator.initIfRequired(servletContext);
192+
}
193+
catch (ServletException ex) {
194+
throw new RuntimeException("Failed to initialize Filter " + filter, ex);
195+
}
196+
}
197+
}
174198

175199
return super.createMockMvc(filterArray, mockServletConfig, wac, this.defaultRequestBuilder,
176200
this.defaultResponseCharacterEncoding, this.globalResultMatchers, this.globalResultHandlers,

spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,8 +17,12 @@
1717
package org.springframework.test.web.servlet.setup;
1818

1919
import java.nio.charset.Charset;
20+
import java.util.EnumSet;
21+
import java.util.Map;
2022

23+
import jakarta.servlet.DispatcherType;
2124
import jakarta.servlet.Filter;
25+
import jakarta.servlet.FilterConfig;
2226

2327
import org.springframework.test.web.servlet.DispatcherServletCustomizer;
2428
import org.springframework.test.web.servlet.MockMvcBuilder;
@@ -37,40 +41,37 @@
3741
public interface ConfigurableMockMvcBuilder<B extends ConfigurableMockMvcBuilder<B>> extends MockMvcBuilder {
3842

3943
/**
40-
* Add filters mapped to any request (i.e. "/*"). For example:
41-
* <pre class="code">
42-
* mockMvcBuilder.addFilters(springSecurityFilterChain);
43-
* </pre>
44-
* <p>It is the equivalent of the following web.xml configuration:
45-
* <pre class="code">
46-
* &lt;filter-mapping&gt;
47-
* &lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
48-
* &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
49-
* &lt;/filter-mapping&gt;
50-
* </pre>
51-
* <p>Filters will be invoked in the order in which they are provided.
44+
* Add filters mapped to all requests. Filters are invoked in the same order.
45+
* <p>Note: if you need the filter to be initialized with {@link Filter#init(FilterConfig)},
46+
* please use {@link #addFilter(Filter, Map, EnumSet, String...)} instead.
5247
* @param filters the filters to add
5348
*/
5449
<T extends B> T addFilters(Filter... filters);
5550

5651
/**
57-
* Add a filter mapped to a specific set of patterns. For example:
58-
* <pre class="code">
59-
* mockMvcBuilder.addFilter(myResourceFilter, "/resources/*");
60-
* </pre>
61-
* <p>It is the equivalent of:
62-
* <pre class="code">
63-
* &lt;filter-mapping&gt;
64-
* &lt;filter-name&gt;myResourceFilter&lt;/filter-name&gt;
65-
* &lt;url-pattern&gt;/resources/*&lt;/url-pattern&gt;
66-
* &lt;/filter-mapping&gt;
67-
* </pre>
68-
* <p>Filters will be invoked in the order in which they are provided.
52+
* Add a filter mapped to specific patterns.
53+
* <p>Note: if you need the filter to be initialized with {@link Filter#init(FilterConfig)},
54+
* please use {@link #addFilter(Filter, Map, EnumSet, String...)} instead.
6955
* @param filter the filter to add
70-
* @param urlPatterns the URL patterns to map to; if empty, "/*" is used by default
56+
* @param urlPatterns the URL patterns to map to; if empty, matches all requests
7157
*/
7258
<T extends B> T addFilter(Filter filter, String... urlPatterns);
7359

60+
/**
61+
* Add a filter that will be initialized via {@link Filter#init(FilterConfig)}
62+
* with the given init parameters, and will also apply only to requests that
63+
* match the given dispatcher types and URL patterns.
64+
* @param filter the filter to add
65+
* @param initParams the init parameters to initialize the filter with
66+
* @param dispatcherTypes dispatcher types the filter applies to
67+
* @param urlPatterns the URL patterns to map to; if empty, matches all requests
68+
* @since 6.1
69+
* @see org.springframework.mock.web.MockFilterConfig
70+
*/
71+
<T extends B> T addFilter(
72+
Filter filter, Map<String, String> initParams,
73+
EnumSet<DispatcherType> dispatcherTypes, String... urlPatterns);
74+
7475
/**
7576
* Define default request properties that should be merged into all
7677
* performed requests. In effect this provides a mechanism for defining
Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,16 +18,22 @@
1818

1919
import java.io.IOException;
2020
import java.util.ArrayList;
21+
import java.util.EnumSet;
2122
import java.util.List;
23+
import java.util.Map;
2224

25+
import jakarta.servlet.DispatcherType;
2326
import jakarta.servlet.Filter;
2427
import jakarta.servlet.FilterChain;
2528
import jakarta.servlet.FilterConfig;
29+
import jakarta.servlet.ServletContext;
2630
import jakarta.servlet.ServletException;
2731
import jakarta.servlet.ServletRequest;
2832
import jakarta.servlet.ServletResponse;
2933
import jakarta.servlet.http.HttpServletRequest;
3034

35+
import org.springframework.lang.Nullable;
36+
import org.springframework.mock.web.MockFilterConfig;
3137
import org.springframework.util.Assert;
3238
import org.springframework.web.util.UrlPathHelper;
3339

@@ -39,14 +45,22 @@
3945
* @author Rob Winch
4046
* @since 3.2
4147
*/
42-
final class PatternMappingFilterProxy implements Filter {
48+
final class MockMvcFilterDecorator implements Filter {
4349

4450
private static final String EXTENSION_MAPPING_PATTERN = "*.";
4551

4652
private static final String PATH_MAPPING_PATTERN = "/*";
4753

4854
private final Filter delegate;
4955

56+
@Nullable
57+
private final Map<String, String> initParams;
58+
59+
@Nullable
60+
private final EnumSet<DispatcherType> dispatcherTypes;
61+
62+
private final boolean hasPatterns;
63+
5064
/** Patterns that require an exact match, e.g. "/test" */
5165
private final List<String> exactMatches = new ArrayList<>();
5266

@@ -58,11 +72,27 @@ final class PatternMappingFilterProxy implements Filter {
5872

5973

6074
/**
61-
* Creates a new instance.
75+
* Create instance with URL patterns only.
76+
* <p>Note: when this constructor is used, the Filter is not initialized.
77+
*/
78+
public MockMvcFilterDecorator(Filter delegate, String[] urlPatterns) {
79+
this(delegate, null, null, urlPatterns);
80+
}
81+
82+
/**
83+
* Create instance with init parameters to initialize the filter with,
84+
* as well as dispatcher types and URL patterns to match.
6285
*/
63-
public PatternMappingFilterProxy(Filter delegate, String... urlPatterns) {
64-
Assert.notNull(delegate, "A delegate Filter is required");
86+
public MockMvcFilterDecorator(
87+
Filter delegate, @Nullable Map<String, String> initParams,
88+
@Nullable EnumSet<DispatcherType> dispatcherTypes, String... urlPatterns) {
89+
90+
Assert.notNull(delegate, "filter cannot be null");
91+
Assert.notNull(urlPatterns, "urlPatterns cannot be null");
6592
this.delegate = delegate;
93+
this.initParams = initParams;
94+
this.dispatcherTypes = dispatcherTypes;
95+
this.hasPatterns = (urlPatterns.length != 0);
6696
for (String urlPattern : urlPatterns) {
6797
addUrlPattern(urlPattern);
6898
}
@@ -96,15 +126,23 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
96126
HttpServletRequest httpRequest = (HttpServletRequest) request;
97127
String requestPath = UrlPathHelper.defaultInstance.getPathWithinApplication(httpRequest);
98128

99-
if (matches(requestPath)) {
129+
if (matchDispatcherType(httpRequest.getDispatcherType()) && matchRequestPath(requestPath)) {
100130
this.delegate.doFilter(request, response, filterChain);
101131
}
102132
else {
103133
filterChain.doFilter(request, response);
104134
}
105135
}
106136

107-
private boolean matches(String requestPath) {
137+
private boolean matchDispatcherType(DispatcherType dispatcherType) {
138+
return (this.dispatcherTypes == null ||
139+
this.dispatcherTypes.stream().anyMatch(type -> type == dispatcherType));
140+
}
141+
142+
private boolean matchRequestPath(String requestPath) {
143+
if (!this.hasPatterns) {
144+
return true;
145+
}
108146
for (String pattern : this.exactMatches) {
109147
if (pattern.equals(requestPath)) {
110148
return true;
@@ -136,4 +174,12 @@ public void destroy() {
136174
this.delegate.destroy();
137175
}
138176

177+
public void initIfRequired(@Nullable ServletContext servletContext) throws ServletException {
178+
if (this.initParams != null) {
179+
MockFilterConfig filterConfig = new MockFilterConfig(servletContext);
180+
this.initParams.forEach(filterConfig::addInitParameter);
181+
this.delegate.init(filterConfig);
182+
}
183+
}
184+
139185
}

0 commit comments

Comments
 (0)