How to Use HandlerInterceptor In Spring MVC

You might have asked yourself the following questions one day How can we intercept a request before it hits the controller? How can I add an attribute, modify something, perform a security check, and so on? In this article, we will explore how to achieve these tasks using the Spring MVC interface HandlerInterceptor. Additionally, we will understand when HandlerInterceptor get invoked within the request processing workflow. Without further ado, let’s dive into our amazing article!

Code Example

Please find the working code example on GitHub.

HandlerMapping & HandlerExecutionChain

Understand the workflow

Before we begin understanding HandlerInterceptor interface, it’s crucial to understand the HandlerMapping and HandlerExecutionChain Spring MVC components.

Here is a straightforward diagram that visually explains the workflow of a request within Spring MVC.

  • The client sends an HTTP request.
  • DispatcherServlet consults the configured HandlerMappings to determine which handler should handle the request(controller, endpoint, …..).
  • If a suitable handler is found, the corresponding HandlerExecutionChain is created (HandlerExecutionChain includes the handler and some interceptors)
  • DispatcherServlet calls the appropriate HandlerAdapter based on the handler type.
  • If the handler returns a logical view name, DispatcherServlet resolves it to an actual view using configured ViewResolvers.
  • view rendering using the model object
  • The rendered view is sent back as an HTTP response to the client.

Please refer to this article for more information.

HandlerExecutionChain Overview

In the previous section, we explored the optimized workflow of the Spring MVC framework, with a focus on the handler execution process. However, we didn’t delve into the interceptors that are part of the HandlerExecutionChain alongside the handler returned from the handler mapping. Let’s now examine these interceptors and understand their purpose in the request processing flow.

Understanding Interceptors

Interceptors are classes that are invoked before hitting the handler. We utilize interceptors when we need to execute specific logic before or after the handler (endpoint method or controller). For example, we can develop an IPInterceptor interceptor, which checks if the HTTP request originates from a blacklisted IP address.

Now, let’s visualize the workflow including these interceptors:

Spring MVC HandlerInterceptor interface

To create an interceptor, we need to implement the HandlerInterceptor interface provided by Spring MVC. Here is the source code for our HandlerInterceptor interface, which comprises three methods. We will implement these methods according to our specific use case:

public interface HandlerInterceptor {<br><br>	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)<br>			throws Exception {<br><br>		return true;<br>	}<br><br>	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,<br>			@Nullable ModelAndView modelAndView) throws Exception {<br>	}<br><br>	<br>	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,<br>			@Nullable Exception ex) throws Exception {<br>	}<br><br>}

Now, let’s delve into each method and understand when they are invoked:

preHandle()

  • This method is invoked before the actual handler method of the controller is called.
  • It can be used to perform pre-processing tasks such as logging, IP whitelisting/blacklisting, etc.
  • If this method returns true, the handler method is executed. If it returns false, the handler execution is skipped, and the response is sent directly (useful for blocking access based on certain conditions).

postHandle()

  • This method is invoked after the handler method has been called but before the view is rendered.
  • It allows you to modify the ModelAndView object before the view is rendered.
  • Useful for tasks like adding attributes to the model, modifying the view, or logging post-processing actions.

afterCompletion()

  • This method is invoked after the view has been rendered (after rendering the view to the client).
  • It is typically used for cleanup tasks or resource release tasks that need to be performed after the request has been completed, regardless of the outcome.
  • The Exception parameter (ex) allows you to handle exceptions that occurred during the execution of the handler method or view rendering.

IPInterceptor Example

Setting Up the Project

In this Spring MVC series, we opt for a non-Spring Boot project to have full control over our setup. Let’s begin by creating a simple Maven web project.

Once the project is created, add the Spring Web MVC dependency to your pom.xml file and synchronize the project:

<dependency><br>    <groupId>org.springframework</groupId><br>    <artifactId>spring-webmvc</artifactId><br>    <version>6.0.0</version><br></dependency><br>

To run the project, ensure you have Tomcat installed and deploy the project to it. After deployment, access your project via the URL http://localhost:8080/project-name/

In a previous article, we discussed the three methods for configuring the DispatcherServlet in Spring 6. You can choose your preferred method; I’ll use the AbstractAnnotationConfigDispatcherServletInitializer approach here.

public class DSConfiguration extends AbstractAnnotationConfigDispatcherServletInitializer {<br><br>    @Override<br>    protected String[] getServletMappings() {<br>        return new String[]{"/"};<br>    }<br><br>    @Override<br>    protected Class<?>[] getRootConfigClasses() {<br>        return new Class[0];<br>    }<br><br>    @Override<br>    protected Class<?>[] getServletConfigClasses() {<br>        return new Class[]{AppConfig.class};<br>    }<br>}<br>

AppConfig is our configuration class used for component scanning and declaring our beans ( ex: ViewResolver Bean).

@Configuration<br>@ComponentScan(basePackages = "com.javalaunchpad")<br>public class AppConfig {<br><br>    @Bean<br>    public ViewResolver viewResolver() {<br>        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();<br>        viewResolver.setPrefix("/WEB-INF/view/");<br>        viewResolver.setSuffix(".jsp");<br>        return viewResolver;<br>    }<br>}<br>

Having completed the setup, we’re now ready to dive into creating our interceptor!

IPInterceptor Interceptor

In this example, we'll create an interceptor to intercept requests and verify if they are not coming from a blacklisted IP address.1
  • IPInterceptor class

The IPInterceptor class implements the HandlerInterceptor interface, providing methods to intercept requests at different stages of processing.

public class IPInterceptor implements HandlerInterceptor {<br><br>    @Override<br>    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {<br>        // Logic to check if the request IP is blacklisted<br>        if (IPUtils.isIpBlacklisted(IPUtils.getClientIpAddr(request))) {<br>            response.setStatus(HttpServletResponse.SC_FORBIDDEN); // Set status to 403 if blacklisted<br>            return false; // Stop further processing<br>        }<br>        return true; // Continue processing<br>    }<br><br>    @Override<br>    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,<br>                           ModelAndView modelAndView) throws Exception {<br>        // Post-handle logic (not used in this example)<br>    }<br><br>    @Override<br>    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)<br>            throws Exception {<br>        // After-completion logic (not used in this example)<br>    }<br>}<br>

  • IPUtils class

The IPUtils class provides utility methods to handle IP-related operations, such as retrieving the client’s IP address and checking if an IP is blacklisted.

public class IPUtils {<br><br>    static String[] blacklistedIps = {"", "", ""}; // Placeholder for blacklisted IPs<br><br>    // Method to get the IP address of the client<br>    public static String getClientIpAddr(HttpServletRequest request) {<br>        String ip = request.getHeader("X-Forwarded-For");<br>        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {<br>            ip = request.getHeader("Proxy-Client-IP");<br>        }<br>        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {<br>            ip = request.getHeader("WL-Proxy-Client-IP");<br>        }<br>        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {<br>            ip = request.getHeader("HTTP_CLIENT_IP");<br>        }<br>        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {<br>            ip = request.getHeader("HTTP_X_FORWARDED_FOR");<br>        }<br>        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {<br>            ip = request.getRemoteAddr();<br>        }<br>        System.out.println("Request IP address: " + ip);<br>        return ip;<br>    }<br><br>    // Method to test if the IP address is blacklisted<br>    public static boolean isIpBlacklisted(String ip) {<br>        for (String blacklistedIp : blacklistedIps) {<br>            if (blacklistedIp.equals(ip)) {<br>                return true;<br>            }<br>        }<br>        return false;<br>    }<br>}<br>

Intercepter Registration

In the AppConfig class, we register IPInterceptor as an interceptor for all requests.

@Configuration<br>@ComponentScan(basePackages = "com.javalaunchpad")<br>@EnableWebMvc<br>public class AppConfig implements WebMvcConfigurer {<br><br>   // Other config Beans <br><br>    @Override<br>    public void addInterceptors(InterceptorRegistry registry) {<br>        registry.addInterceptor(new IPInterceptor());<br>    }<br>}<br>

In non-Spring Boot applications, using ‘@EnableWebMvc’ is important. Without it, the interception won’t work!

NOTE: we can apply IPInterceptor only to specific URLs by using addPathPatterns method. This allows you to define patterns for which the interceptor should be applied, providing more fine-grained control over request interception based on URL patterns.

@Override<br>    public void addInterceptors(InterceptorRegistry registry) {<br>        registry.addInterceptor(new IPInterceptor()).addPathPatterns("/test");<br>    }

In this example, we are registering the interceptor only for the /test path.

Conclusion

In this Spring MVC guide, we’ve explored the power of the HandlerInterceptor interface in Spring MVC, to implement interceptors for pre-processing and post-processing HTTP requests. Incorporating interceptors adds a layer of control and customization.

Leave a Reply

Your email address will not be published. Required fields are marked *

Join the Tribe