Affects: 2.7.9 - 3.2.3 (Likely more, I just tested the latest and a random 2.7)
When using @WebFilter with either the value or urlPatterns fields defined, Spring fails to respect them. It would make sense that the following behaves the same.
@Webfilter("/rest/example/*")
@Component
public static class SimpleFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request, response);
}
}
// Should behavior similar to
@Webfilter
@Component
public static class SimpleFilter extends OncePerRequestFilter {
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return !request.getRequestURI().startsWith("/rest/example");
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request, response);
}
}
A repository configured with all three WebFilter variations and tests for them can be found here.
Comment From: snicoll
Thanks for the report but that's the wrong issue tracker. Moving to Spring Boot.
From a quick debugging session, those filters are indeed taken into account by SpringBootMockMvcBuilderCustomizer but their urlPatterns collection is empty.
Comment From: wilkinsona
Mixing Spring's component model and the Servlet spec's component model isn't supported. This means that your sample also doesn't work as you would like when running its main method as the url patterns are ignored there too.
If you want to use @WebFilter you should not use @Component. Instead, you should enable scanning for servlet components using @ServletComponentScan:
@ServletComponentScan
@SpringBootApplication
public class WebfilterBugApplication {
public static void main(String[] args) {
SpringApplication.run(WebfilterBugApplication.class, args);
}
@WebFilter("/not/simple")
public static class SimpleFilterOne extends OncePerRequestFilter {
public static boolean CALLED = false;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("One");
CALLED = true;
filterChain.doFilter(request, response);
}
}
@WebFilter
public static class SimpleFilterTwo extends OncePerRequestFilter {
public static boolean CALLED = false;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("Two");
CALLED = true;
filterChain.doFilter(request, response);
}
}
@WebFilter(urlPatterns = "/not/simple")
public static class SimpleFilterThree extends OncePerRequestFilter {
public static boolean CALLED = false;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("Three");
CALLED = true;
filterChain.doFilter(request, response);
}
}
@RestController
@RequestMapping("/rest/simple")
public static class SimpleController {
@GetMapping
public String getString() {
return "Hello World";
}
}
}
This will result in the @WebFilters being found and their attributes being honored when it's started using its main method. Unfortunately, it doesn't fix the tests as I have just discovered that the registration of servlet components doesn't work in a mock web environment. It works with @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) but not with the default mock environment. We can use this issue to fix that.
Comment From: askneller
@wilkinsona I'd like to take at look at this if that's ok.
Comment From: askneller
According to the ServletComponentScan docs, scanning is only performed when using an embedded server.
https://github.com/spring-projects/spring-boot/blob/b4208edd4e270981a951ca3f64d8cab9d29c24c2/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentScan.java#L33-L36
And according to the MOCK WebEnvironment it is not using an embedded server.
https://github.com/spring-projects/spring-boot/blob/b4208edd4e270981a951ca3f64d8cab9d29c24c2/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTest.java#L139-L184
So that at least explains why the WebFilters are not picked up. Not sure if you still want the filters to be excluded from the MOCK environment.
Comment From: wilkinsona
Thanks for taking a look, @askneller. What you have described is this crux of the problem and I'm not yet sure how best to fix it. I think it's reasonable to expect @ServletComponentScan to work with a mock web environment, but that expectation does contradict the current documentation. I think the core team will have to spend some time discussing this one before we can move forwards.
Comment From: philwebb
I think the embedded boolean in WebEnvironment isn't very well named. It's designed to trigger the creation of a ServletWebServerApplicationContext which we don't want for mock environments.
I think we'll need to fix this at the @ServletComponentScan level.