Spring MVC - Intercepting requests with a HandlerInterceptor

[Updated: Dec 9, 2016, Created: Mar 16, 2016]

The implementation of interface org.springframework.web.servlet.HandlerInterceptor is used to intercept requests to the controllers.

public interface HandlerInterceptor {

	boolean preHandle(HttpServletRequest request,
                          HttpServletResponse response,
                          Object handler) throws Exception;

	void postHandle(HttpServletRequest request,
                        HttpServletResponse response,
                        Object handler,
                        ModelAndView modelAndView) throws Exception;

	void afterCompletion(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler,
                             Exception ex) throws Exception;

}
  • preHandle: is called before the target handler method is invoked for a given request. If this method returns false, further processing is abandoned i.e. the handler method is not called.
  • postHandle: is called after execution of target handler, but before the view is rendered. Good for post processing of what we started in preHandler method e.g. performance logging.
  • afterCompletion: is called after rendering the view. Good for resource cleanups


HandlerInterceptor vs Servlet Filter

HandlerInterceptor is basically similar to a Servlet Filter, but in contrast to the latter it just allows custom pre-processing with the option of prohibiting the execution of the handler itself, and custom post-processing. Filters are more powerful, for example they allow for exchanging the request and response objects that are handed down the chain. Note that a filter gets configured in web.xml, a HandlerInterceptor in the application context.

As a basic guideline, fine-grained handler-related preprocessing tasks are candidates for HandlerInterceptor implementations, especially factored-out common handler code and authorization checks. On the other hand, a Filter is well-suited for request content and view content handling, like multipart forms and GZIP compression. This typically shows when one needs to map the filter to certain content types (e.g. images), or to all requests.


In the following example we are going to show how to implement authentication using HandlerInterceptor. If the clients wants to access a page without logged in, the request is redirected to the login page.



The Login HandlerInterceptor

In this example we are going to use HandlerInterceptorAdapter which implements HandlerInterceptor interface. HandlerInterceptorAdapter is based on adapter design pattern.

public class LoginInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle (HttpServletRequest request,
                              HttpServletResponse response,
                              Object handler) throws Exception {
        RequestMapping rm = ((HandlerMethod) handler).getMethodAnnotation(
                            RequestMapping.class);

        boolean alreadyLoggedIn = request.getSession()
                                         .getAttribute("user") != null;
        boolean loginPageRequested = rm != null && rm.value().length > 0
                                              && "login".equals(rm.value()[0]);


        if (alreadyLoggedIn && loginPageRequested) {
            response.sendRedirect(request.getContextPath() + "/app/main-age");
            return false;
        } else if (!alreadyLoggedIn && !loginPageRequested) {
            response.sendRedirect(request.getContextPath() + "/login");
            return false;
        }

        return true;
    }
}



Registering the Interceptor

@EnableWebMvc
@Configuration
public class MyWebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors (InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor());
    }

    ......

}



The Controller

@Controller
@RequestMapping
public class AppController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "login", method = RequestMethod.GET)
    public String handleLoginGetRequest () {
        return "user-login";
    }

    @RequestMapping(value = "login", method = RequestMethod.POST)
    public String handleLoginPostRequest (User user, Model model,
                                          HttpServletRequest request) {

        User loggedUser = userService.loginUser(user);
        if (loggedUser != null) {
            request.getSession(true)
                   .setAttribute("user", loggedUser);
            return "redirect:/app/main-page";
        } else {
            model.addAttribute("error", "Not a valid user");
            return "user-login";
        }
    }

    @RequestMapping(value = "app/**", method = RequestMethod.GET)
    public String handleAppRequest (Model model, HttpServletRequest request) {
        model.addAttribute("uri", request.getRequestURI());
        model.addAttribute("user", request.getAttribute("user"));
        return "app-page";
    }
}

In above example, we are just mapping all application related requests to handleAppRequest handler method. In practice we will have more handler methods instead.




user-login.jsp

<%@ page language="java"
    contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<html>
<head>
<style>
.error {
   color: red;
}
</style>
</head>
<body>

<h3> Login Page <h3>
<br/>
<form action="login" method="post" >
<pre>
         Email address <input type="text" name="emailAddress" />
              Password <input type="password" name="password" />
                        <input type="submit" value="Submit" />
                        <div class = "error">${error}</div>
</pre>
</form>
</body>
</html>



app-page.jsp

<%@ page language="java"
    contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<html>
<body>
<h3> App Page </h3>
<p>${uri}</p>
<br/>
<h4>User</h4>
<p>User Name: ${user.name}</p>
<p>User email: ${user.emailAddress}
</body>
</html>



Example Project

Dependencies and Technologies Used :

  • Spring Web MVC 4.2.4.RELEASE: Spring Web MVC.
  • Spring TestContext Framework 4.2.4.RELEASE: Spring TestContext Framework.
  • Java Servlet API 3.0.1
  • JUnit 4.12: JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.
  • JDK 1.8
  • Maven 3.0.4

Spring Handler Interceptor Select All Download
  • spring-handler-interceptor
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • webapp
          • WEB-INF
            • views
      • test
        • java
          • com
            • logicbig
              • example

To Test the Application run the junit tests.

Or

Run the web application by using embedded tomcat:

mvn  clean install tomcat7:run-war


Enter 'admin@example.com' for email address and 'password' for password:

See Also