In software development there is a big number of cases when you need somehow to catch a request coming to server and do some pre-processing or post-processing. For example, it is must for authentication, when you need to validate if user was granted to access the specific path before this path’s execution. Spring provides as a part of Spring Web a way to do it – using SpringInterceptor objects. They allow to do provide a custom logic before or after controller processes a handler.

This post describes a definition of Spring’s HandlerInterceptor, how it is different from Java Servlet’s HttpFilters, as well an overview of HandlerInterceptor interface methods and how to configure them in your applications.

What is Spring HanlderInterceptor?

HandlerInterceptor is a Spring Web interface, that allows to implement components to intercept client requests and to provide a custom processing logic. Such processing can be executed prior or after Spring controllers process requests. Handler interceptors implement org.springframework.web.servlet.HandlerInterceptor interface that offers three core methods:

  • boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler)
  • void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView mw)
  • void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex)

Developers can register any number of interceptors, as well attach them to specific group of handlers and control their order. Such functionality relates HandlerInterceptors with another tool – HttpFilters.

Creation of HandlerInterceptor

As it was mentioned, in order to create an interceptor, developer has to implement HanlderInterceptor interface. As all its methods are default, you can provide implementation for all of them or only for the needed one. HanlderInterceptor are often used to implement token-based authentication. Let have a look on a basic case of using HandlerInterceptor:

@Component
public class TokenInterceptor implements HandlerInterceptor {

    @Autowired private AuthService service;

    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
        String authToken = req.getHeader("Authorization");
        if (authToken==null) throw new RuntimeException();
        return service.validateToken(authToken);
    }
}

In this code snippet we created a custom interceptor component to check that incoming requests do have valid authentication token attached. Please note, that unlike HttpFilters, HandlerInterceptor provide an access to HttpServletRequest and HttpServletResponse components, so you can use them directly without necessity to cast from ServletRequest and ServletResponse.

We also annotated TokenInterceptor class as Spring’s @Component to make the framework know to treat it in this manner in annotation-based configuration.

Interface methods

The interface provides three default methods. This section provides an overview of them.

preHandle()

This method intercepts an execution of the request handling. It is fired before controllers process the request and returns a boolean value, that is used to determine next processing. If true is returned by preHandle() method, then request goes to handler. In case of false return, request is interrupted. This method accepts three arguments:

  • HttpServletRequest is a current HTTP request
  • HttpServletResponse is a current HTTP response
  • Handler is the chosen handler object to handle the request

As we talked before, this method can be used to validate pre-conditions, such as tokens. We also can pass information to handlers using HttpServletRequest. Take a look on the code snippet below:

@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
    String token = req.getHeader("Authorization");
    Optional<User> user = service.parseToken(token);
    if (user.isPresent()){
        req.setAttribute("user", user.get());
        return true;
    } else {
        return false;
    }
}

postHandle()

This method is fired after handler processing by Spring controllers, but before rendering the view (if you build an application, that follows MVC architecture). So in this case you can modify the ModelAndView that is going to be displayed and attach additional data. This method accepts four arguments:

  • HttpServletRequest is a current HTTP request
  • HttpServletResponse is a current HTTP response
  • Handler is the chosen handler object to handle the request
  • ModelAndView is the returned by the handler ModelAndView object.

As an example of using such method we can provide a situation, when we want to display to the user a time used to render a page. For this we also need first to capture an initial timing using preHandle. Take a look on the following snippet:

@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
    long started = System.currentTimeMillis();
    req.setAttribute("startedTime", started);
    return true;
}

@Override
public void postHandle postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView modelAndView) throws Exception {
    long started = Long.valueOf(req.getAttribute("startedTime").toString());
    long finished = System.currentTimeMillis();
    long rendered = finished - started;
    modelAndView.addObject("renderedTime", rendered);
}

afterCompletion()

Finally, the third method here – afterCompletion() is called after handler was processed and after view was rendered to the user. Please note, that this method is called at any case of handler execution, but only if the corresponding preHandle() method returned true. It accepts four arguments:

  • HttpServletRequest is a current HTTP request
  • HttpServletResponse is a current HTTP response
  • Handler is the chosen handler object to handle the request
  • Exception is an exception thrown by handler, unless, it was handled through an exception resolver

We can use such method to log result of handler processing like in the following code:

// assume we have a logger
private static final Logger logger = LoggerFactory.getLogger(SampleInterceptor.class);

@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex)
    throws Exception{
        String destination = req.getRequestUrl();
        int result = res.getStatus();
        logger.info("The request to the destination {} was processed with the result {}", destination, result);
}

Configuration

In order to use the handler interceptor we need to configure it using a custom instance of WebMvcConfigurer configuration. Here is a code that demonstrates a basic usage of token interceptor configuration:

@Configuration
public class TokenInterceptorConfig implements WebMvcConfigurer {

    @Autowired
    private TokenInterceptor tokenInterceptor;

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

}

This code attachs the interceptor to all handlers. We also annotated it with Spring’s @Configuration to tell the framework that it is used to define beans and Spring container will use in bean generation. Although, this is the basic case. We also can:

  • Attach interceptors to specific routes/groups of routes
  • Exclude interceptors from specific routes/groups of routes
  • Define an order of interceptors execution

For this we need to manipulate InterceptorRegistration object returned as a result of addInterceptor().

Include paths

To attach interceptors for specific paths or groups of routes we can utilize addPathPatterns() method that allows to specify certain paths. It comes in two overloaded versions:

  • addPathPatterns(List<String> patterns) that accepts a group of patterns in a form of the array list
  • addPathPatterns(String... patterns) that accepts patterns in a form of varagrs

As we have a token interceptor, we want to apply it to all routes that starts with /secured/:

@Override 
public void addInterceptors(InterceptorRegistry registry){
    registry.addInterceptor(tokenInterceptor).addPathPatterns("/secured/**");
}

In this code snippet, we use here double asterisk to tell Spring to include paths

Exclude paths

Similarly, we can exclude some paths from using the interceptor. It can be done using excludePathPatterns() method, which can be in two forms:

  • excludePathPatterns(List<String> patterns) that accepts patterns in form of list
  • excludePathPatterns(String... patterns) that accepts patterns as varargs

For instance we want to exclude paths /login, /signup and /restore-password from token validation (as they don’t yet have any security):

@Override 
public void addInterceptors(InterceptorRegistry registry){
    List<String> excludedRoutes = Arrays.asList("/login", "/signup", "/restore-password");
    registry.addInterceptor(tokenInterceptor).addPathPatterns("/secured/**").excludePathPatterns(excludedRoutes);
}

Define order

Finally, if we use several HandlerInterceptors we can specify an order they are executed. This is done by calling order() method from InterceptorRegistration that accepts an int number of interceptor’s order (started from 0). For instance we have three interceptors: one to validate tokens, second to validate user’s subscription status and finally to log:

@Configuration
public class AppInterceptorConfig implements WebMvcConfigurer {

    //.. inject interceptors
    @Autowired private TokenInterceptor tokenInterceptor;
    @Autowired private SubscriptionInterceptor subscriptionInterceptor;
    @Autowired private LoggingInterceptor logInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor).order(0);
        registry.addInterceptor(subscriptionInterceptor).order(1);
        registry.addInterceptor(logInterceptor).order(2);
    }

}

Conclusion

In this post we looked on Spring Web’s HandlerInterceptor interface that is used to intercept client requests and to provide a custom processing logic. We also compared it with a similar tool – HttpFilter that comes from JavaEE and is independent from Spring. We validated methods that this interface provides as well how to configure it for usage in Spring apps.

Do you want to increase your Java collections skills?

This topic is really huge, and a single post is not enough to cover all. That is why I wrote this Practical Guide. Everything you need in the one book. Do you want to become Java ninja?

References

  • Iuliana Cosmina et. all Pro Spring 5 5th edn, Apress, 2017
  • The Essentials of Filters Oracle Technology Network, read here