Skip to content

Commit

Permalink
MockMvc supports filter initParams and DispatcherType's
Browse files Browse the repository at this point in the history
  • Loading branch information
rstoyanchev committed Oct 11, 2023
1 parent 0542fe5 commit 776e28a
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 53 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -55,7 +55,10 @@ protected final MockMvc createMockMvc(Filter[] filters, MockServletConfig servle
List<ResultMatcher> globalResultMatchers, List<ResultHandler> globalResultHandlers,
@Nullable List<DispatcherServletCustomizer> dispatcherServletCustomizers) {

MockMvc mockMvc = createMockMvc(filters, servletConfig, webAppContext, defaultRequestBuilder, globalResultMatchers, globalResultHandlers, dispatcherServletCustomizers);
MockMvc mockMvc = createMockMvc(
filters, servletConfig, webAppContext, defaultRequestBuilder,
globalResultMatchers, globalResultHandlers, dispatcherServletCustomizers);

mockMvc.setDefaultResponseCharacterEncoding(defaultResponseCharacterEncoding);
return mockMvc;
}
Expand All @@ -75,7 +78,7 @@ protected final MockMvc createMockMvc(Filter[] filters, MockServletConfig servle
dispatcherServlet.init(servletConfig);
}
catch (ServletException ex) {
// should never happen..
// should never happen...
throw new MockMvcBuildException("Failed to initialize TestDispatcherServlet", ex);
}

Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,10 +18,14 @@

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;

import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;

import org.springframework.lang.Nullable;
import org.springframework.mock.web.MockServletConfig;
Expand Down Expand Up @@ -76,9 +80,9 @@ public abstract class AbstractMockMvcBuilder<B extends AbstractMockMvcBuilder<B>
@Override
public final <T extends B> T addFilters(Filter... filters) {
Assert.notNull(filters, "filters cannot be null");
for (Filter f : filters) {
Assert.notNull(f, "filters cannot contain null values");
this.filters.add(f);
for (Filter filter : filters) {
Assert.notNull(filter, "filters cannot contain null values");
this.filters.add(filter);
}
return self();
}
Expand All @@ -88,12 +92,22 @@ public final <T extends B> T addFilter(Filter filter, String... urlPatterns) {
Assert.notNull(filter, "filter cannot be null");
Assert.notNull(urlPatterns, "urlPatterns cannot be null");
if (urlPatterns.length > 0) {
filter = new PatternMappingFilterProxy(filter, urlPatterns);
filter = new MockMvcFilterDecorator(filter, urlPatterns);
}
this.filters.add(filter);
return self();
}

@Override
public <T extends B> T addFilter(
Filter filter, Map<String, String> initParams,
EnumSet<DispatcherType> dispatcherTypes, String... urlPatterns) {

filter = new MockMvcFilterDecorator(filter, initParams, dispatcherTypes, urlPatterns);
this.filters.add(filter);
return self();
}

@Override
public final <T extends B> T defaultRequest(RequestBuilder requestBuilder) {
this.defaultRequestBuilder = requestBuilder;
Expand Down Expand Up @@ -171,6 +185,16 @@ public final MockMvc build() {
}

Filter[] filterArray = this.filters.toArray(new Filter[0]);
for (Filter filter : filterArray) {
if (filter instanceof MockMvcFilterDecorator filterDecorator) {
try {
filterDecorator.initIfRequired(servletContext);
}
catch (ServletException ex) {
throw new RuntimeException("Failed to initialize Filter " + filter, ex);
}
}
}

return super.createMockMvc(filterArray, mockServletConfig, wac, this.defaultRequestBuilder,
this.defaultResponseCharacterEncoding, this.globalResultMatchers, this.globalResultHandlers,
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,8 +17,12 @@
package org.springframework.test.web.servlet.setup;

import java.nio.charset.Charset;
import java.util.EnumSet;
import java.util.Map;

import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterConfig;

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

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

/**
* Add a filter mapped to a specific set of patterns. For example:
* <pre class="code">
* mockMvcBuilder.addFilter(myResourceFilter, "/resources/*");
* </pre>
* <p>It is the equivalent of:
* <pre class="code">
* &lt;filter-mapping&gt;
* &lt;filter-name&gt;myResourceFilter&lt;/filter-name&gt;
* &lt;url-pattern&gt;/resources/*&lt;/url-pattern&gt;
* &lt;/filter-mapping&gt;
* </pre>
* <p>Filters will be invoked in the order in which they are provided.
* 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.
* @param filter the filter to add
* @param urlPatterns the URL patterns to map to; if empty, "/*" is used by default
* @param urlPatterns the URL patterns to map to; if empty, matches all requests
*/
<T extends B> T addFilter(Filter filter, String... urlPatterns);

/**
* Add a filter that will be initialized via {@link Filter#init(FilterConfig)}
* 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 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
* @since 6.1
* @see org.springframework.mock.web.MockFilterConfig
*/
<T extends B> T addFilter(
Filter filter, Map<String, String> initParams,
EnumSet<DispatcherType> dispatcherTypes, String... urlPatterns);

/**
* Define default request properties that should be merged into all
* performed requests. In effect this provides a mechanism for defining
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,16 +18,22 @@

import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;

import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;

import org.springframework.lang.Nullable;
import org.springframework.mock.web.MockFilterConfig;
import org.springframework.util.Assert;
import org.springframework.web.util.UrlPathHelper;

Expand All @@ -39,14 +45,22 @@
* @author Rob Winch
* @since 3.2
*/
final class PatternMappingFilterProxy implements Filter {
final class MockMvcFilterDecorator implements Filter {

private static final String EXTENSION_MAPPING_PATTERN = "*.";

private static final String PATH_MAPPING_PATTERN = "/*";

private final Filter delegate;

@Nullable
private final Map<String, String> initParams;

@Nullable
private final EnumSet<DispatcherType> dispatcherTypes;

private final boolean hasPatterns;

/** Patterns that require an exact match, e.g. "/test" */
private final List<String> exactMatches = new ArrayList<>();

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


/**
* Creates a new instance.
* Create instance with URL patterns only.
* <p>Note: when this constructor is used, the Filter is not initialized.
*/
public MockMvcFilterDecorator(Filter delegate, String[] urlPatterns) {
this(delegate, null, null, urlPatterns);
}

/**
* Create instance with init parameters to initialize the filter with,
* as well as dispatcher types and URL patterns to match.
*/
public PatternMappingFilterProxy(Filter delegate, String... urlPatterns) {
Assert.notNull(delegate, "A delegate Filter is required");
public MockMvcFilterDecorator(
Filter delegate, @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.dispatcherTypes = dispatcherTypes;
this.hasPatterns = (urlPatterns.length != 0);
for (String urlPattern : urlPatterns) {
addUrlPattern(urlPattern);
}
Expand Down Expand Up @@ -96,15 +126,23 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestPath = UrlPathHelper.defaultInstance.getPathWithinApplication(httpRequest);

if (matches(requestPath)) {
if (matchDispatcherType(httpRequest.getDispatcherType()) && matchRequestPath(requestPath)) {
this.delegate.doFilter(request, response, filterChain);
}
else {
filterChain.doFilter(request, response);
}
}

private boolean matches(String requestPath) {
private boolean matchDispatcherType(DispatcherType dispatcherType) {
return (this.dispatcherTypes == null ||
this.dispatcherTypes.stream().anyMatch(type -> type == dispatcherType));
}

private boolean matchRequestPath(String requestPath) {
if (!this.hasPatterns) {
return true;
}
for (String pattern : this.exactMatches) {
if (pattern.equals(requestPath)) {
return true;
Expand Down Expand Up @@ -136,4 +174,12 @@ public void destroy() {
this.delegate.destroy();
}

public void initIfRequired(@Nullable ServletContext servletContext) throws ServletException {
if (this.initParams != null) {
MockFilterConfig filterConfig = new MockFilterConfig(servletContext);
this.initParams.forEach(filterConfig::addInitParameter);
this.delegate.init(filterConfig);
}
}

}

0 comments on commit 776e28a

Please sign in to comment.