Browse Source

Merge pull request #46605 from nosan

* gh-46605:
  Add support for the @FilterRegistration annotation with @WebMvcTest

Closes gh-46605
pull/46673/head
Andy Wilkinson 5 months ago
parent
commit
e7effe4de4
  1. 42
      spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/SpringBootMockMvcBuilderCustomizer.java
  2. 116
      spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/WebMvcTestServletFilterRegistrationIntegrationTests.java
  3. 8
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletContextInitializerBeans.java

42
spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/servlet/SpringBootMockMvcBuilderCustomizer.java

@ -20,10 +20,13 @@ import java.io.PrintStream; @@ -20,10 +20,13 @@ import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import jakarta.servlet.Filter;
import jakarta.servlet.annotation.WebInitParam;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -31,11 +34,13 @@ import org.springframework.beans.factory.ListableBeanFactory; @@ -31,11 +34,13 @@ import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.web.servlet.AbstractFilterRegistrationBean;
import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
import org.springframework.boot.web.servlet.FilterRegistration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.RegistrationBean;
import org.springframework.boot.web.servlet.ServletContextInitializerBeans;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultHandler;
import org.springframework.test.web.servlet.result.PrintingResultHandler;
@ -330,18 +335,49 @@ public class SpringBootMockMvcBuilderCustomizer implements MockMvcBuilderCustomi @@ -330,18 +335,49 @@ public class SpringBootMockMvcBuilderCustomizer implements MockMvcBuilderCustomi
@Override
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter(beanFactory));
}
private static final class FilterRegistrationBeanAdapter implements RegistrationBeanAdapter<Filter> {
private final ListableBeanFactory beanFactory;
private FilterRegistrationBeanAdapter(ListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public RegistrationBean createRegistrationBean(String name, Filter source, int totalNumberOfSourceBeans) {
public RegistrationBean createRegistrationBean(String beanName, Filter source,
int totalNumberOfSourceBeans) {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(source);
bean.setName(name);
bean.setName(beanName);
FilterRegistration registrationAnnotation = this.beanFactory.findAnnotationOnBean(beanName,
FilterRegistration.class);
if (registrationAnnotation != null) {
// Supports @Order annotation on @Bean methods
Order orderAnnotation = this.beanFactory.findAnnotationOnBean(beanName, Order.class);
Assert.state(orderAnnotation != null, "'orderAnnotation' must not be null");
configureFromAnnotation(bean, registrationAnnotation, orderAnnotation);
}
return bean;
}
private void configureFromAnnotation(FilterRegistrationBean<Filter> bean,
FilterRegistration registrationAnnotation, Order orderAnnotation) {
bean.setEnabled(registrationAnnotation.enabled());
bean.setOrder(orderAnnotation.value());
if (StringUtils.hasText(registrationAnnotation.name())) {
bean.setName(registrationAnnotation.name());
}
if (registrationAnnotation.dispatcherTypes().length > 0) {
bean.setDispatcherTypes(EnumSet.copyOf(Arrays.asList(registrationAnnotation.dispatcherTypes())));
}
for (WebInitParam param : registrationAnnotation.initParameters()) {
bean.addInitParameter(param.name(), param.value());
}
bean.setUrlPatterns(Arrays.asList(registrationAnnotation.urlPatterns()));
}
}
}

116
spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/servlet/mockmvc/WebMvcTestServletFilterRegistrationIntegrationTests.java

@ -0,0 +1,116 @@ @@ -0,0 +1,116 @@
/*
* Copyright 2012-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.autoconfigure.web.servlet.mockmvc;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.web.servlet.FilterRegistration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.web.filter.OncePerRequestFilter;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link FilterRegistration} and {@link FilterRegistrationBean} with
* {@link WebMvcTest @WebMvcTest}.
*
* @author Dmytro Nosan
*/
@WebMvcTest
class WebMvcTestServletFilterRegistrationIntegrationTests {
@Autowired
private MockMvcTester mvc;
@Test
void annotation() {
assertThat(this.mvc.get().uri("/annotation")).headers()
.hasValue("name", "annotation")
.hasValue("param1", "value1")
.hasValue("param2", "value2")
.doesNotContainHeader("param3")
.doesNotContainHeader("param4");
}
@Test
void registration() {
assertThat(this.mvc.get().uri("/registration")).headers()
.hasValue("name", "registration")
.hasValue("param3", "value3")
.hasValue("param4", "value4")
.doesNotContainHeader("param1")
.doesNotContainHeader("param2");
}
@TestConfiguration(proxyBeanMethods = false)
static class FilterRegistrationConfiguration {
@Bean
@FilterRegistration(name = "annotation", urlPatterns = "/annotation",
initParameters = { @WebInitParam(name = "param1", value = "value1"),
@WebInitParam(name = "param2", value = "value2") })
@Order(SecurityProperties.DEFAULT_FILTER_ORDER - 1)
TestFilter testFilterAnnotationBean() {
return new TestFilter();
}
@Bean
FilterRegistrationBean<TestFilter> testFilterRegistrationBean() {
FilterRegistrationBean<TestFilter> registration = new FilterRegistrationBean<>(new TestFilter());
registration.setName("registration");
registration.addUrlPatterns("/registration");
registration.setInitParameters(Map.of("param3", "value3", "param4", "value4"));
registration.setOrder(SecurityProperties.DEFAULT_FILTER_ORDER - 1);
return registration;
}
}
private static final class TestFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
response.addHeader("name", getFilterName());
FilterConfig config = getFilterConfig();
if (config != null) {
Collections.list(config.getInitParameterNames())
.forEach((name) -> response.addHeader(name, config.getInitParameter(name)));
}
filterChain.doFilter(request, response);
}
}
}

8
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletContextInitializerBeans.java

@ -330,7 +330,13 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo @@ -330,7 +330,13 @@ public class ServletContextInitializerBeans extends AbstractCollection<ServletCo
}
/**
* {@link RegistrationBeanAdapter} for {@link Filter} beans.
* {@link RegistrationBeanAdapter} implementation for {@link Filter} beans.
* <p>
* <b>NOTE:</b> A similar implementation is used in
* {@code SpringBootMockMvcBuilderCustomizer} for registering
* {@code @FilterRegistration} beans with {@code @MockMvc}. If you modify this class,
* please also update {@code SpringBootMockMvcBuilderCustomizer} if needed.
* </p>
*/
private static class FilterRegistrationBeanAdapter implements RegistrationBeanAdapter<Filter> {

Loading…
Cancel
Save