MockMvcBuilder supports filter name in addition to initParams
Closes gh-31474
This commit is contained in:
		
							parent
							
								
									ef47eef92a
								
							
						
					
					
						commit
						e57b942b4d
					
				| 
						 | 
				
			
			@ -100,10 +100,10 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	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) {
 | 
			
		||||
 | 
			
		||||
		filter = new MockMvcFilterDecorator(filter, initParams, dispatcherTypes, urlPatterns);
 | 
			
		||||
		filter = new MockMvcFilterDecorator(filter, filterName, initParams, dispatcherTypes, urlPatterns);
 | 
			
		||||
		this.filters.add(filter);
 | 
			
		||||
		return self();
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ import jakarta.servlet.DispatcherType;
 | 
			
		|||
import jakarta.servlet.Filter;
 | 
			
		||||
import jakarta.servlet.FilterConfig;
 | 
			
		||||
 | 
			
		||||
import org.springframework.lang.Nullable;
 | 
			
		||||
import org.springframework.test.web.servlet.DispatcherServletCustomizer;
 | 
			
		||||
import org.springframework.test.web.servlet.MockMvcBuilder;
 | 
			
		||||
import org.springframework.test.web.servlet.RequestBuilder;
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +52,7 @@ public interface ConfigurableMockMvcBuilder<B extends ConfigurableMockMvcBuilder
 | 
			
		|||
	/**
 | 
			
		||||
	 * Add a filter mapped to specific patterns.
 | 
			
		||||
	 * <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 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
 | 
			
		||||
	 * match the given dispatcher types and URL patterns.
 | 
			
		||||
	 * @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 dispatcherTypes dispatcher types the filter applies to
 | 
			
		||||
	 * @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
 | 
			
		||||
	 */
 | 
			
		||||
	<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);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,7 @@ import java.util.ArrayList;
 | 
			
		|||
import java.util.EnumSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.DispatcherType;
 | 
			
		||||
import jakarta.servlet.Filter;
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +57,7 @@ final class MockMvcFilterDecorator implements Filter {
 | 
			
		|||
	private final Filter delegate;
 | 
			
		||||
 | 
			
		||||
	@Nullable
 | 
			
		||||
	private final Map<String, String> initParams;
 | 
			
		||||
	private final Function<ServletContext, FilterConfig> filterConfigInitializer;
 | 
			
		||||
 | 
			
		||||
	@Nullable
 | 
			
		||||
	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.
 | 
			
		||||
	 */
 | 
			
		||||
	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.
 | 
			
		||||
	 */
 | 
			
		||||
	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) {
 | 
			
		||||
 | 
			
		||||
		Assert.notNull(delegate, "filter cannot be null");
 | 
			
		||||
		Assert.notNull(urlPatterns, "urlPatterns cannot be null");
 | 
			
		||||
		this.delegate = delegate;
 | 
			
		||||
		this.initParams = initParams;
 | 
			
		||||
		this.filterConfigInitializer = getFilterConfigInitializer(filterName, initParams);
 | 
			
		||||
		this.dispatcherTypes = dispatcherTypes;
 | 
			
		||||
		this.hasPatterns = (urlPatterns.length != 0);
 | 
			
		||||
		for (String urlPattern : urlPatterns) {
 | 
			
		||||
			addUrlPattern(urlPattern);
 | 
			
		||||
		}
 | 
			
		||||
		this.hasPatterns = initPatterns(urlPatterns);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void addUrlPattern(String urlPattern) {
 | 
			
		||||
		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 = "/";
 | 
			
		||||
	private static Function<ServletContext, FilterConfig> getFilterConfigInitializer(
 | 
			
		||||
			@Nullable String filterName, @Nullable Map<String, String> initParams) {
 | 
			
		||||
 | 
			
		||||
		return servletContext -> {
 | 
			
		||||
			MockFilterConfig filterConfig = (filterName != null ?
 | 
			
		||||
					new MockFilterConfig(servletContext, filterName) : new MockFilterConfig(servletContext));
 | 
			
		||||
			if (initParams != null) {
 | 
			
		||||
				initParams.forEach(filterConfig::addInitParameter);
 | 
			
		||||
			}
 | 
			
		||||
			return filterConfig;
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private boolean initPatterns(String... urlPatterns) {
 | 
			
		||||
		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 {
 | 
			
		||||
		if (this.initParams != null) {
 | 
			
		||||
			MockFilterConfig filterConfig = new MockFilterConfig(servletContext);
 | 
			
		||||
			this.initParams.forEach(filterConfig::addInitParameter);
 | 
			
		||||
		if (this.filterConfigInitializer != null) {
 | 
			
		||||
			FilterConfig filterConfig = this.filterConfigInitializer.apply(servletContext);
 | 
			
		||||
			this.delegate.init(filterConfig);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,14 +66,14 @@ public class MockMvcFilterDecoratorTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	public void init() throws Exception {
 | 
			
		||||
		FilterConfig config = new MockFilterConfig();
 | 
			
		||||
		filter = new MockMvcFilterDecorator(delegate, null, null, "/");
 | 
			
		||||
		filter = new MockMvcFilterDecorator(delegate, new String[] {"/"});
 | 
			
		||||
		filter.init(config);
 | 
			
		||||
		assertThat(delegate.filterConfig).isEqualTo(config);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void destroy() {
 | 
			
		||||
		filter = new MockMvcFilterDecorator(delegate, null, null, "/");
 | 
			
		||||
		filter = new MockMvcFilterDecorator(delegate, new String[] {"/"});
 | 
			
		||||
		filter.destroy();
 | 
			
		||||
		assertThat(delegate.destroy).isTrue();
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -251,7 +251,7 @@ public class MockMvcFilterDecoratorTests {
 | 
			
		|||
 | 
			
		||||
		request.setDispatcherType(requestDispatcherType);
 | 
			
		||||
		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);
 | 
			
		||||
 | 
			
		||||
		assertThat(delegate.request).isNull();
 | 
			
		||||
| 
						 | 
				
			
			@ -265,7 +265,7 @@ public class MockMvcFilterDecoratorTests {
 | 
			
		|||
 | 
			
		||||
	private void assertFilterInvoked(String requestUri, String pattern) throws Exception {
 | 
			
		||||
		request.setRequestURI(request.getContextPath() + requestUri);
 | 
			
		||||
		filter = new MockMvcFilterDecorator(delegate, null, null, pattern);
 | 
			
		||||
		filter = new MockMvcFilterDecorator(delegate, new String[] {pattern});
 | 
			
		||||
		filter.doFilter(request, response, filterChain);
 | 
			
		||||
 | 
			
		||||
		assertThat(delegate.request).isEqualTo(request);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -134,7 +134,7 @@ class StandaloneMockMvcBuilderTests {
 | 
			
		|||
		ArgumentCaptor<FilterConfig> captor = ArgumentCaptor.forClass(FilterConfig.class);
 | 
			
		||||
 | 
			
		||||
		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();
 | 
			
		||||
 | 
			
		||||
		verify(filter, times(1)).init(captor.capture());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -132,22 +132,12 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		if (binderFactory != null && (arg != null || !hasDefaultValue)) {
 | 
			
		||||
			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());
 | 
			
		||||
			}
 | 
			
		||||
			arg = convertIfNecessary(parameter, webRequest, binderFactory, namedValueInfo, arg);
 | 
			
		||||
			// Check for null value after conversion of incoming argument value
 | 
			
		||||
			if (arg == null) {
 | 
			
		||||
				if (namedValueInfo.defaultValue != null) {
 | 
			
		||||
					arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
 | 
			
		||||
					arg = convertIfNecessary(parameter, webRequest, binderFactory, namedValueInfo, arg);
 | 
			
		||||
				}
 | 
			
		||||
				else if (namedValueInfo.required && !nestedParameter.isOptional()) {
 | 
			
		||||
					handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest);
 | 
			
		||||
| 
						 | 
				
			
			@ -284,6 +274,25 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
 | 
			
		|||
		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.
 | 
			
		||||
	 * @param arg the resolved argument value
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue