Spring-framework: RequestDispatcher forward not working with OncePerRequestFilter with Spring boot 2.3.* version but working fine till 2.2.10

Created on 25 Sep 2020  路  10Comments  路  Source: spring-projects/spring-framework

Here the scenarios is -
Filter1 -> filter2 -> Filter3 -> Controller

If filter 1 - (condition == true )
then skip all filters and go to controller -
_request.getRequestDispatcher(request.getServletPath()).forward(request, response);_
else
go to next chain in filter
_filterChain.doFilter(request, response);_

With spring-boot 2.3.* - Its not working as with condition-1, it always going in filter-chain but with spring-boot version till 2.2.10- its working as expected.

Please advise ?

web

Most helpful comment

@spring-issuemaster ??

All 10 comments

Could you provide a sample to work with?

Since Spring Boot 2.3 all OncePerRequestFilters are attached to all dispatch types. See https://github.com/spring-projects/spring-boot/issues/18953 for the discussion on that.

Could you provide a sample to work with?

https://github.com/hakuna16/Spring-Boot-Sample-Project/tree/master
Here you can find out the dummy project setup for this scenario.

In the Read me you can find out how to replicate the issue.

More Information
https://github.com/hakuna16/Spring-Boot-Sample-Project/blob/master/README.md

@rstoyanchev
please find the attached sample project.

Also Please let me know if you need direct code here instead of the sample code.

Also Please provide your sugessions.

Its working fine after overriding this method-shouldNotFilterAsyncDispatch from filter class.

I debugged this issue and found that due to this change at

org.springframework.boot.web.servlet.AbstractFilterRegistrationBean
All dispatch types are attached with OncePerRequestFilter

protected void configure(FilterRegistration.Dynamic registration) {
        super.configure(registration);
        EnumSet<DispatcherType> dispatcherTypes = this.dispatcherTypes;
        if (dispatcherTypes == null) {
            T filter = getFilter();
            if (ClassUtils.isPresent("org.springframework.web.filter.OncePerRequestFilter",
                    filter.getClass().getClassLoader()) && filter instanceof OncePerRequestFilter) {
                dispatcherTypes = EnumSet.allOf(DispatcherType.class);
            }
            else {
                dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
            }
        }

so all filters execution are happening with request dispatch as forward as configured with all dispatch types as per this code-

org.apache.catalina.core.ApplicationFilterFactory.createFilterChain(ServletRequest, Wrapper, Servlet)  

As this will include all filters with request dispatch-type as forward.

Presently done changes with customising registering bean with FilterRegistrationBean and explicitly defining filter dispatcher type with request only.

Anything configurable solution ? as have to change all filter to be explicitly declared and registered as bean.

@spring-issuemaster ??

@hakuna16, the idea with the change in Boot is that a OncePerRequestFilter should know what dispatcher types it handles based on its internal protected methods and checks and therefore it can be registered for all.

You will need to encode it in your filter somehow. For example both Filter2 and Filter3 could check for the "Test" header and either participate or not. Or Filter3 could skip FORWARD dispatcher type, etc.

@rstoyanchev
Don't you think it's a kind of hack we are having in the code?

What if we make it somehow configurable for the dispatcher types??

@hakuna16 in my experience whether a Filter cares about a particular dispatch type depends on the nature of what it does and not on the details of a specific request. Hence OncePerRequestFilter exposes methods to indicate whether the concrete sub-class supports ASYNC or ERROR dispatch types. In turn Boot registers all OncePerRequestFilter for all dispatcher types since such filters support all dispatch types, in the sense of being prepared to do process or skip.

I do think that your current solution is somewhat unusual. Having one Filter cause the next one to be bypassed and doing so by forwarding where that the next Filter is not involved in FORWARD dispatches. If that is indeed your goal, I think passing a hint from the first to the second filter (like a request attribute), is probably a more accurate way to model this but I don't have a full grasp of your context, and there might be other ways too to structure the code.

I would also ask does your second filter really need to extend OncePerRequestFilter? I'm not sure what you're gaining from it and Boot wouldn't automatically register your filter for all dispatcher types if it wasn't a OncePerRequestFilter for which it makes more assumptions.

Was this page helpful?
0 / 5 - 0 ratings