Browse Source

MockMvcBuilder supports filter name in addition to initParams

Closes gh-31474
pull/31518/head
rstoyanchev 2 years ago
parent
commit
e57b942b4d
  1. 4
      spring-test/src/main/java/org/springframework/test/web/servlet/setup/AbstractMockMvcBuilder.java
  2. 8
      spring-test/src/main/java/org/springframework/test/web/servlet/setup/ConfigurableMockMvcBuilder.java
  3. 72
      spring-test/src/main/java/org/springframework/test/web/servlet/setup/MockMvcFilterDecorator.java
  4. 8
      spring-test/src/test/java/org/springframework/test/web/servlet/setup/MockMvcFilterDecoratorTests.java
  5. 2
      spring-test/src/test/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilderTests.java
  6. 33
      spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java

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

@ -100,10 +100,10 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>
@Override @Override
public <T extends B> T addFilter( public <T extends B> T addFilter(
Filter filter, Map<String, String> initParams, Filter filter, @Nullable String filterName, Map<String, String> initParams,
EnumSet<DispatcherType> dispatcherTypes, String... urlPatterns) { EnumSet<DispatcherType> dispatcherTypes, String... urlPatterns) {
filter = new MockMvcFilterDecorator(filter, initParams, dispatcherTypes, urlPatterns); filter = new MockMvcFilterDecorator(filter, filterName, initParams, dispatcherTypes, urlPatterns);
this.filters.add(filter); this.filters.add(filter);
return self(); return self();
} }

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

@ -24,6 +24,7 @@ import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter; import jakarta.servlet.Filter;
import jakarta.servlet.FilterConfig; import jakarta.servlet.FilterConfig;
import org.springframework.lang.Nullable;
import org.springframework.test.web.servlet.DispatcherServletCustomizer; import org.springframework.test.web.servlet.DispatcherServletCustomizer;
import org.springframework.test.web.servlet.MockMvcBuilder; import org.springframework.test.web.servlet.MockMvcBuilder;
import org.springframework.test.web.servlet.RequestBuilder; import org.springframework.test.web.servlet.RequestBuilder;
@ -51,7 +52,7 @@ public interface ConfigurableMockMvcBuilder<B extends ConfigurableMockMvcBuilder
/** /**
* Add a filter mapped to specific patterns. * Add a filter mapped to specific patterns.
* <p>Note: if you need the filter to be initialized with {@link Filter#init(FilterConfig)}, * <p>Note: if you need the filter to be initialized with {@link Filter#init(FilterConfig)},
* please use {@link #addFilter(Filter, Map, EnumSet, String...)} instead. * please use {@link #addFilter(Filter, String, Map, EnumSet, String...)} instead.
* @param filter the filter to add * @param filter the filter to add
* @param urlPatterns the URL patterns to map to; if empty, matches all requests * @param urlPatterns the URL patterns to map to; if empty, matches all requests
*/ */
@ -62,6 +63,9 @@ public interface ConfigurableMockMvcBuilder<B extends ConfigurableMockMvcBuilder
* with the given init parameters, and will also apply only to requests that * with the given init parameters, and will also apply only to requests that
* match the given dispatcher types and URL patterns. * match the given dispatcher types and URL patterns.
* @param filter the filter to add * @param filter the filter to add
* @param filterName the name to use for the filter; if {@code null}, then
* {@link org.springframework.mock.web.MockFilterConfig} is created without
* a name, which defaults to an empty String for the name
* @param initParams the init parameters to initialize the filter with * @param initParams the init parameters to initialize the filter with
* @param dispatcherTypes dispatcher types the filter applies to * @param dispatcherTypes dispatcher types the filter applies to
* @param urlPatterns the URL patterns to map to; if empty, matches all requests * @param urlPatterns the URL patterns to map to; if empty, matches all requests
@ -69,7 +73,7 @@ public interface ConfigurableMockMvcBuilder<B extends ConfigurableMockMvcBuilder
* @see org.springframework.mock.web.MockFilterConfig * @see org.springframework.mock.web.MockFilterConfig
*/ */
<T extends B> T addFilter( <T extends B> T addFilter(
Filter filter, Map<String, String> initParams, Filter filter, @Nullable String filterName, Map<String, String> initParams,
EnumSet<DispatcherType> dispatcherTypes, String... urlPatterns); EnumSet<DispatcherType> dispatcherTypes, String... urlPatterns);
/** /**

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

@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
import jakarta.servlet.DispatcherType; import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter; import jakarta.servlet.Filter;
@ -56,7 +57,7 @@ final class MockMvcFilterDecorator implements Filter {
private final Filter delegate; private final Filter delegate;
@Nullable @Nullable
private final Map<String, String> initParams; private final Function<ServletContext, FilterConfig> filterConfigInitializer;
@Nullable @Nullable
private final EnumSet<DispatcherType> dispatcherTypes; private final EnumSet<DispatcherType> dispatcherTypes;
@ -78,7 +79,12 @@ final class MockMvcFilterDecorator implements Filter {
* <p>Note: when this constructor is used, the Filter is not initialized. * <p>Note: when this constructor is used, the Filter is not initialized.
*/ */
public MockMvcFilterDecorator(Filter delegate, String[] urlPatterns) { public MockMvcFilterDecorator(Filter delegate, String[] urlPatterns) {
this(delegate, null, null, urlPatterns); Assert.notNull(delegate, "filter cannot be null");
Assert.notNull(urlPatterns, "urlPatterns cannot be null");
this.delegate = delegate;
this.filterConfigInitializer = null;
this.dispatcherTypes = null;
this.hasPatterns = initPatterns(urlPatterns);
} }
/** /**
@ -86,38 +92,51 @@ final class MockMvcFilterDecorator implements Filter {
* as well as dispatcher types and URL patterns to match. * as well as dispatcher types and URL patterns to match.
*/ */
public MockMvcFilterDecorator( public MockMvcFilterDecorator(
Filter delegate, @Nullable Map<String, String> initParams, Filter delegate, @Nullable String filterName, @Nullable Map<String, String> initParams,
@Nullable EnumSet<DispatcherType> dispatcherTypes, String... urlPatterns) { @Nullable EnumSet<DispatcherType> dispatcherTypes, String... urlPatterns) {
Assert.notNull(delegate, "filter cannot be null"); Assert.notNull(delegate, "filter cannot be null");
Assert.notNull(urlPatterns, "urlPatterns cannot be null"); Assert.notNull(urlPatterns, "urlPatterns cannot be null");
this.delegate = delegate; this.delegate = delegate;
this.initParams = initParams; this.filterConfigInitializer = getFilterConfigInitializer(filterName, initParams);
this.dispatcherTypes = dispatcherTypes; this.dispatcherTypes = dispatcherTypes;
this.hasPatterns = (urlPatterns.length != 0); this.hasPatterns = initPatterns(urlPatterns);
for (String urlPattern : urlPatterns) {
addUrlPattern(urlPattern);
}
} }
private void addUrlPattern(String urlPattern) { private static Function<ServletContext, FilterConfig> getFilterConfigInitializer(
Assert.notNull(urlPattern, "Found null URL Pattern"); @Nullable String filterName, @Nullable Map<String, String> initParams) {
if (urlPattern.startsWith(EXTENSION_MAPPING_PATTERN)) {
this.endsWithMatches.add(urlPattern.substring(1)); return servletContext -> {
} MockFilterConfig filterConfig = (filterName != null ?
else if (urlPattern.equals(PATH_MAPPING_PATTERN) || urlPattern.equals(ALL_MAPPING_PATTERN)) { new MockFilterConfig(servletContext, filterName) : new MockFilterConfig(servletContext));
this.startsWithMatches.add(""); if (initParams != null) {
} initParams.forEach(filterConfig::addInitParameter);
else if (urlPattern.endsWith(PATH_MAPPING_PATTERN)) { }
this.startsWithMatches.add(urlPattern.substring(0, urlPattern.length() - 1)); return filterConfig;
this.exactMatches.add(urlPattern.substring(0, urlPattern.length() - 2)); };
} }
else {
if (urlPattern.isEmpty()) { private boolean initPatterns(String... urlPatterns) {
urlPattern = "/"; for (String urlPattern : urlPatterns) {
Assert.notNull(urlPattern, "Found null URL Pattern");
if (urlPattern.startsWith(EXTENSION_MAPPING_PATTERN)) {
this.endsWithMatches.add(urlPattern.substring(1));
}
else if (urlPattern.equals(PATH_MAPPING_PATTERN) || urlPattern.equals(ALL_MAPPING_PATTERN)) {
this.startsWithMatches.add("");
}
else if (urlPattern.endsWith(PATH_MAPPING_PATTERN)) {
this.startsWithMatches.add(urlPattern.substring(0, urlPattern.length() - 1));
this.exactMatches.add(urlPattern.substring(0, urlPattern.length() - 2));
}
else {
if (urlPattern.isEmpty()) {
urlPattern = "/";
}
this.exactMatches.add(urlPattern);
} }
this.exactMatches.add(urlPattern);
} }
return (urlPatterns.length != 0);
} }
@ -177,9 +196,8 @@ final class MockMvcFilterDecorator implements Filter {
} }
public void initIfRequired(@Nullable ServletContext servletContext) throws ServletException { public void initIfRequired(@Nullable ServletContext servletContext) throws ServletException {
if (this.initParams != null) { if (this.filterConfigInitializer != null) {
MockFilterConfig filterConfig = new MockFilterConfig(servletContext); FilterConfig filterConfig = this.filterConfigInitializer.apply(servletContext);
this.initParams.forEach(filterConfig::addInitParameter);
this.delegate.init(filterConfig); this.delegate.init(filterConfig);
} }
} }

8
spring-test/src/test/java/org/springframework/test/web/servlet/setup/MockMvcFilterDecoratorTests.java

@ -66,14 +66,14 @@ public class MockMvcFilterDecoratorTests {
@Test @Test
public void init() throws Exception { public void init() throws Exception {
FilterConfig config = new MockFilterConfig(); FilterConfig config = new MockFilterConfig();
filter = new MockMvcFilterDecorator(delegate, null, null, "/"); filter = new MockMvcFilterDecorator(delegate, new String[] {"/"});
filter.init(config); filter.init(config);
assertThat(delegate.filterConfig).isEqualTo(config); assertThat(delegate.filterConfig).isEqualTo(config);
} }
@Test @Test
public void destroy() { public void destroy() {
filter = new MockMvcFilterDecorator(delegate, null, null, "/"); filter = new MockMvcFilterDecorator(delegate, new String[] {"/"});
filter.destroy(); filter.destroy();
assertThat(delegate.destroy).isTrue(); assertThat(delegate.destroy).isTrue();
} }
@ -251,7 +251,7 @@ public class MockMvcFilterDecoratorTests {
request.setDispatcherType(requestDispatcherType); request.setDispatcherType(requestDispatcherType);
request.setRequestURI(request.getContextPath() + requestUri); request.setRequestURI(request.getContextPath() + requestUri);
filter = new MockMvcFilterDecorator(delegate, null, EnumSet.of(filterDispatcherType), pattern); filter = new MockMvcFilterDecorator(delegate, null, null, EnumSet.of(filterDispatcherType), pattern);
filter.doFilter(request, response, filterChain); filter.doFilter(request, response, filterChain);
assertThat(delegate.request).isNull(); assertThat(delegate.request).isNull();
@ -265,7 +265,7 @@ public class MockMvcFilterDecoratorTests {
private void assertFilterInvoked(String requestUri, String pattern) throws Exception { private void assertFilterInvoked(String requestUri, String pattern) throws Exception {
request.setRequestURI(request.getContextPath() + requestUri); request.setRequestURI(request.getContextPath() + requestUri);
filter = new MockMvcFilterDecorator(delegate, null, null, pattern); filter = new MockMvcFilterDecorator(delegate, new String[] {pattern});
filter.doFilter(request, response, filterChain); filter.doFilter(request, response, filterChain);
assertThat(delegate.request).isEqualTo(request); assertThat(delegate.request).isEqualTo(request);

2
spring-test/src/test/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilderTests.java

@ -134,7 +134,7 @@ class StandaloneMockMvcBuilderTests {
ArgumentCaptor<FilterConfig> captor = ArgumentCaptor.forClass(FilterConfig.class); ArgumentCaptor<FilterConfig> captor = ArgumentCaptor.forClass(FilterConfig.class);
MockMvcBuilders.standaloneSetup(new PersonController()) MockMvcBuilders.standaloneSetup(new PersonController())
.addFilter(filter, Map.of("p", "v"), EnumSet.of(DispatcherType.REQUEST), "/") .addFilter(filter, null, Map.of("p", "v"), EnumSet.of(DispatcherType.REQUEST), "/")
.build(); .build();
verify(filter, times(1)).init(captor.capture()); verify(filter, times(1)).init(captor.capture());

33
spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java

@ -132,22 +132,12 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
} }
if (binderFactory != null && (arg != null || !hasDefaultValue)) { if (binderFactory != null && (arg != null || !hasDefaultValue)) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name); arg = convertIfNecessary(parameter, webRequest, binderFactory, namedValueInfo, arg);
try {
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
// Check for null value after conversion of incoming argument value // Check for null value after conversion of incoming argument value
if (arg == null) { if (arg == null) {
if (namedValueInfo.defaultValue != null) { if (namedValueInfo.defaultValue != null) {
arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
arg = convertIfNecessary(parameter, webRequest, binderFactory, namedValueInfo, arg);
} }
else if (namedValueInfo.required && !nestedParameter.isOptional()) { else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest); handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest);
@ -284,6 +274,25 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
return value; return value;
} }
private static Object convertIfNecessary(
MethodParameter parameter, NativeWebRequest webRequest, WebDataBinderFactory binderFactory,
NamedValueInfo namedValueInfo, Object arg) throws Exception {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
return arg;
}
/** /**
* Invoked after a value is resolved. * Invoked after a value is resolved.
* @param arg the resolved argument value * @param arg the resolved argument value

Loading…
Cancel
Save