LS Price Increase Launch

The Price of all “Learn Spring” course packages will increase by $40 on next Friday:

>> GET ACCESS NOW

1. Overview

In this tutorial, we'll learn about OncePerRequestFilter, a special type of filter in Spring. We will see what problem it solves and understand how to use it through a quick example.

2. What Is OncePerRequestFilter?

Let's first understand how filters work. A Filter can be called either before or after servlet execution. When a request is dispatched to a servlet, the RequestDispatcher may forward it to another servlet. There's a possibility that the other servlet also has the same filter. In such scenarios, the same filter gets invoked multiple times.

But, we might want to ensure that a specific filter is invoked only once per request. A common use case is when working with Spring Security. When a request goes through the filter chain, we might want some of the authentication actions to happen only once for the request.

We can extend the OncePerRequestFilter in such situations. Spring guarantees that the OncePerRequestFilter is executed only once for a given request.

3. Using OncePerRequestFilter for Synchronous Requests

Let’s take an example to understand how to use this filter. We'll define a class AuthenticationFilter that extends the OncePerRequestFilter, and override the doFilterInternal() method:

public class AuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain) throws
            ServletException, IOException {
        String usrName = request.getHeader(“userName”);
        logger.info("Successfully authenticated user  " +
                userName);
        filterChain.doFilter(request, response);
    }
}

Since the OncePerRequestFilter only supports HTTP requests, there's no need to cast the request and response objects as we do when implementing the Filter interface.

4. Using OncePerRequestFilter for Asynchronous Requests

For asynchronous requests, OncePerRequestFilter doesn’t get applied by default. We need to override the methods shouldNotFilterAsyncDispatch()andshouldNotFilterErrorDispatch() to support this.

Sometimes, we need the filter applied only in the initial request thread and not in the additional threads created in the async dispatch. Other times, we may need to invoke the filter at least once in each additional thread. In such cases, we need to override the shouldNotFilterAsyncDispatch() method.

If the shouldNotFilterAsyncDispatch() method returns true, then the filter will not be called for the subsequent async dispatch. However, if it returns false, the filter will be invoked for each async dispatch, exactly once per thread.

Similarly, we would override the shouldNotFilterErrorDispatch() method and return true or false, depending on whether we want to filter error dispatches or not:

@Component
public class AuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(
      HttpServletRequest request,
      HttpServletResponse response,
      FilterChain filterChain) throws ServletException, IOException {
        String usrName = request.getHeader("userName");
        logger.info("Successfully authenticated user  " +
          usrName);
        filterChain.doFilter(request, response);
    }

    @Override
    protected boolean shouldNotFilterAsyncDispatch() {
        return false;
    }

    @Override
    protected boolean shouldNotFilterErrorDispatch() {
        return false;
    }
}

5. Conditionally Skipping Requests

We can have the filter applied conditionally for some specific requests only and skipped for other requests by overriding the shouldNotFilter() method:

@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
    return Boolean.TRUE.equals(request.getAttribute(SHOULD_NOT_FILTER));
}

6. Quick Example

Let's look at a quick example to understand the behavior of OncePerRequestFilter.
To start with, we'll define a Controller that processes the request asynchronously using Spring's DeferredResult:

@Controller
public class HelloController  {
    @GetMapping(path = "/greeting")
    public DeferredResult<String> hello(HttpServletResponse response) throws Exception {
        DeferredResult<String> deferredResult = new DeferredResult<>();
        executorService.submit(() -> perform(deferredResult));
        return deferredResult;
    }
    private void perform(DeferredResult<String> dr) {
        // some processing 
        dr.setResult("OK");
    }
}

When processing requests asynchronously, both threads go through the same filter chain. Consequently, the filter is called twice: first, when the container thread processes the request, and then after the async dispatcher completes. Once the async processing is completed, the response is returned to the client.

Now, let's define a Filter implementing OncePerRequestFilter:

@Component
public class MyOncePerRequestFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
        logger.info("Inside Once Per Request Filter originated by request {}", request.getRequestURI());
        filterChain.doFilter(request, response);
    }

    @Override
    protected boolean shouldNotFilterAsyncDispatch() {
        return true;
    }
}

In the above code, we have intentionally returned true from the shouldNotFilterAsyncDispatch() method. This is to demonstrate that our filter is invoked only once for the container thread and not for subsequent async threads.

Let's invoke our endpoint to demonstrate this:

curl -X GET http://localhost:8082/greeting 

Output:

10:23:24.175 [http-nio-8082-exec-1] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
10:23:24.175 [http-nio-8082-exec-1] INFO  o.s.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
10:23:24.176 [http-nio-8082-exec-1] INFO  o.s.web.servlet.DispatcherServlet - Completed initialization in 1 ms
10:23:26.814 [http-nio-8082-exec-1] INFO  c.b.O.MyOncePerRequestFilter - Inside OncePer Request Filter originated by request /greeting

Now, let's see the case where we want both the request and the async dispatches to invoke our filter. We just need to override shouldNotFilterAsyncDispatch() to return false to achieve this:

@Override
protected boolean shouldNotFilterAsyncDispatch() {
    return false;
}

Output:

2:53.616 [http-nio-8082-exec-1] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
10:32:53.616 [http-nio-8082-exec-1] INFO  o.s.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
10:32:53.617 [http-nio-8082-exec-1] INFO  o.s.web.servlet.DispatcherServlet - Completed initialization in 1 ms
10:32:53.633 [http-nio-8082-exec-1] INFO  c.b.O.MyOncePerRequestFilter - Inside OncePer Request Filter originated by request /greeting
10:32:53.663 [http-nio-8082-exec-2] INFO  c.b.O.MyOncePerRequestFilter - Inside OncePer Request Filter originated by request /greeting

We can see from the above output that our filter got invoked two times — first by the container thread and then by another thread.

7. Conclusion

In this article, we looked at OncePerRequestFilter, what problems it solves, and how to implement it with some practical examples.

As usual, the complete source code is available over on GitHub.

LS Price Increase Launch

The Price of all “Learn Spring” course packages will increase by $40 on next Friday:

>> GET ACCESS NOW
Generic footer banner
guest
0 Comments
Inline Feedbacks
View all comments