1. Introduction
In this tutorial, the main focus will be to understand the purpose of the chain.doFilter() method in the Spring framework.
To gain a good understanding we’ll first explore what a filter is, what a filter chain is and what are some good use cases for utilizing filters. We’ll then discuss the purpose and importance of a chain.doFilter() method. Additionally, we’ll take a look at how to create our custom filters in Spring.
Finally, we’ll explore the correlation with one of the behavioral design patterns – Chain of Responsibility.
2. What Is a Filter in Spring?
In Spring applications, filters are based on Java Servlet Filters which represent objects that intercept requests and responses. Filters are part of the Java Servlet API and play an important role in web applications because they sit between the client and the server processing logic.
Using them, we can perform tasks before a request reaches the servlet or after a response is generated. Common use cases for filters include:
- Authentication and Authorization
- Auditing and Logging
- Request/Response Modifications
Although not part of the Spring framework, filters are fully compatible with it. We can register them as Spring Beans and use them in our applications. Spring offers a few implementations of filters, some of the common ones include OncePerRequestFilter and CorsFilter.
3. Understanding the chain.doFilter() Method
Before we get into the chain.doFilter() method, it’s important to first understand the concept of the filter chain and its role in the filtering process.
A filter chain represents the sequential flow of processing logic applied to an incoming request or an outgoing response. In other words, it’s a collection of filters that are used to pre-process a request or post-process the response. The filters are arranged in a well-defined order, ensuring that each one can execute its processing logic before passing the request or response to the next stage in the chain.
To define a filter chain, we use the interface FilterChain from Java Servlet API, which contains our method of interest. If we examine the method’s signature, we can see that the request and response objects are defined as input parameters:
void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
The chain.doFilter() method passes the request and response to the next filter in the chain. If no filters remain in the filter chain, a request is forwarded to the target resource, usually a servlet, and a response is sent to the client.
3.1. Why Is It Important to Call chain.doFilter()?
As we can see, the method chain.doFilter() plays an essential role as it ensures that the request continues through all filters in the chain. If we omit the method call, the request won’t proceed to the subsequent filters or the servlet. This could lead to unexpected application behavior, as any logic handled by the subsequent filters wouldn’t be reached.
On the other hand, in cases where authentication or authorization fails, it might benefit us to skip method invocation and break the chain.
4. The Chain of Responsibility Pattern
Now that we understand what chain.doFilter() enables us to do, let’s briefly look at its connection to the Chain of Responsibility pattern. We won’t go into much detail, rather we’ll describe what the pattern is and how it’s related to our topic.
The Chain of Responsibility pattern is a design pattern that focuses on interactions between objects. It explains how handlers, which are individual processing components, can be organized sequentially to process a request. Each handler performs a specific task and then decides whether to pass the request to the next handler for further processing.
If we compare pattern logic with servlet filters we’ll notice that filters are a real-world example of the Chain of Responsibility pattern. Each filter acts as a separate handler, responsible for a part of the processing logic.
The benefits of using this pattern include flexibility as we can add, remove, or reorder filters without modifying other components. Furthermore, the Chain of Responsibility pattern improves the separation of concerns by allowing filters to focus on a single task.
5. Implementing Custom Filters
Implementing custom filters is fairly simple, we’ll need to follow a few steps. In our example, let’s create two simple filters with log statements and showcase the chain.doFilter() method usage in practice.
5.1. Create Filter
First, we must implement a Filter interface and override the method doFilter().
An important thing to note is that this method isn’t the same as the doFilter() method inside the filter chain. The filter’s doFilter() method serves as the entry point where the specific processing logic for the filter should be implemented, while chain.doFilter() is used to pass the request and response to the next filter in the chain.
Here’s how we can implement filters and use the @Order annotation to arrange them:
@Order(1)
@Component
public class FirstFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
LOG.info("Processing the First Filter");
// Omit chain.doFilter() on purpose
}
}
@Order(2)
@Component
public class SecondFilter implements Filter {
@Override
public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
LOG.info("Processing the Second Filter");
chain.doFilter(request, response);
}
}
5.2. Method chain.doFilter() in Action
At this point, when executing any request in our application we can see that the response isn’t returned. The reason for that is that we broke the chain by omitting the chain.doFilter() invocation. If we observe the console, we’ll notice that the log from the second filter is missing:
11:02:35.253 [main] INFO c.baeldung.chaindofilter.FirstFilter - Processing the First Filter
To restore the filter chain, we should include a method call in the first filter:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
LOG.info("Processing the First Filter");
chain.doFilter(request, response);
}
In this case, the first filter successfully passes the request and response to the second filter, and the filter chain has a continuous flow.
Let’s observe the console once again and confirm log statements are present:
11:02:59.330 [main] INFO c.baeldung.chaindofilter.FirstFilter - Processing the First Filter
11:02:59.330 [main] INFO c.baeldung.chaindofilter.SecondFilter - Processing the Second Filter
5.3. Register Filter
Notably, we annotated our filter with the @Component annotation to create a Spring Bean. This step is important because it allows us to register the filter in the servlet container.
There is another way to register filters, which offers more control and customization and it can be achieved by utilizing the FilterRegistrationBean class. Using that class we’re able to specify URL patterns and define the order of the filters.
6. Conclusion
In this article, we explored filters, the filter chain, and the correct use of the chain.doFilter() method. Also, we saw how to create and register our own custom filters in Spring.
Additionally, we discussed the connection to the Chain of Responsibility pattern and highlighted the flexibility and separation of concerns that filters provide.
As always, full code examples are available over on GitHub.
The post What Is chain.doFilter() Doing in Spring Filter? first appeared on Baeldung.