Xenonite
Xenonite

Reputation: 1957

Spring Boot RestController: Intercept incoming requests

I am currently writing some kind of Framework that would allow others to write REST Controllers for it. Naturally, I want those "others" to have as little interaction with whats happening in my code as possible.

Specifically, I want and need to access the requests data (i.e. RequestEntity before the request is handled by the rest controller. Sort of "intercepting" a request before it is handled by the controller, and only then letting the controller handle it.

Consider the following code:

@RestController
@RequestMapping("/")
public class MyController {

    @GetMapping("/")
    @ResponseBody
    public ResponseEntity<String> getSomething(RequestEntity requestEntity) {

        MyClass.doStuffWithRequestEntity(requestEntity);
        // ...

Now what I would need is that ExternalClass.doStuffWithRequestEntity(requestEntity); is called without the need to explicitly call it. Is it possible to have some method in some class being called (with the RequestEntity passed to it!) without having to call it explicitly?

Furthermore, said Interceptor class should create and configure an object that is then again made available to the rest controller.

I'd be thinking something like

class RestController {
    @RestController
    @RequestMapping("/")
    public class MyController {

        @GetMapping("/")
        @ResponseBody
        public ResponseEntity<String> getSomething() {

            MyClass x = MyClass.getInstanceCreatedByInterceptor();
        }
    }
}

and

class Interceptor {
    public void doStuffWithRequestEntity(requestEntity) {

        MyClass x = new MyClass();
        x.fillObjectWithData();
    }
}

being executed before.

The idea is that EVERY (!) incoming request is parsed and its contents get decoded without the programmer of the rest controller getting having to care about this at all. They should just access data via/from the MyClass instance.

Is there a way to do this?

Upvotes: 7

Views: 22923

Answers (3)

RAJKUMAR NAGARETHINAM
RAJKUMAR NAGARETHINAM

Reputation: 1528

import javax.servlet.http.HttpServletRequest ;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

@Configuration
public class AuthenticationHandlerInterceptor implements HandlerInterceptor {

    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
    }

    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
            throws Exception {
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {

        if (validrequest) {
         //fill data here add pass to next level
            return true;
        } else {
      // if you opt to not to proceed the request further you can simply return false here
            return false;
        }
    }

}

prehandle() – called before the actual handler is executed, but the view is not generated yet postHandle() – called after the handler is executed

afterCompletion() – called after the complete request has finished and view was generated

Upvotes: 3

Xenonite
Xenonite

Reputation: 1957

For all those who end up here with the same or a similar question, here is a working minimal example for a SpringBoot (2.1.4) application with an Interceptor:

Minimal.java:

@SpringBootApplication
public class Minimal
{
    public static void main(String[] args)
    {
        SpringApplication.run(Minimal.class, args);
    }
}

MinimalController.java:

@RestController
@RequestMapping("/")
public class Controller
{
    @GetMapping("/")
    @ResponseBody
    public ResponseEntity<String> getMinimal()
    {
        System.out.println("MINIMAL: GETMINIMAL()");

        return new ResponseEntity<String>("returnstring", HttpStatus.OK);
    }
}

Config.java:

@Configuration
public class Config implements WebMvcConfigurer
{
    //@Autowired
    //MinimalInterceptor minimalInterceptor;

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

MinimalInterceptor.java:

public class MinimalInterceptor extends HandlerInterceptorAdapter
{
    @Override
    public boolean preHandle(HttpServletRequest requestServlet, HttpServletResponse responseServlet, Object handler) throws Exception
    {
        System.out.println("MINIMAL: INTERCEPTOR PREHANDLE CALLED");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
    {
        System.out.println("MINIMAL: INTERCEPTOR POSTHANDLE CALLED");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception
    {
        System.out.println("MINIMAL: INTERCEPTOR AFTERCOMPLETION CALLED");
    }
}

works as advertised

The output will give you something like:

> Task :Minimal.main()

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.4.RELEASE)

2019-04-29 11:53:47.560  INFO 4593 --- [           main] io.minimal.Minimal                       : Starting Minimal on y with PID 4593 (/x/y/z/spring-minimal/build/classes/java/main started by x in /x/y/z/spring-minimal)
2019-04-29 11:53:47.563  INFO 4593 --- [           main] io.minimal.Minimal                       : No active profile set, falling back to default profiles: default
2019-04-29 11:53:48.745  INFO 4593 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-04-29 11:53:48.780  INFO 4593 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-04-29 11:53:48.781  INFO 4593 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.17]
2019-04-29 11:53:48.892  INFO 4593 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-04-29 11:53:48.893  INFO 4593 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1269 ms
2019-04-29 11:53:49.130  INFO 4593 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-04-29 11:53:49.375  INFO 4593 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-04-29 11:53:49.380  INFO 4593 --- [           main] io.minimal.Minimal                       : Started Minimal in 2.525 seconds (JVM running for 2.9)
2019-04-29 11:54:01.267  INFO 4593 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-04-29 11:54:01.267  INFO 4593 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2019-04-29 11:54:01.286  INFO 4593 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 19 ms
MINIMAL: INTERCEPTOR PREHANDLE CALLED
MINIMAL: GETMINIMAL()
MINIMAL: INTERCEPTOR POSTHANDLE CALLED
MINIMAL: INTERCEPTOR AFTERCOMPLETION CALLED

Upvotes: 7

Ananthapadmanabhan
Ananthapadmanabhan

Reputation: 6216

Spring-boot allows us to configure custom interceptors.Usually in a spring boot application everything is auto configured and in such cases we can customize it by using the WebMvcConfigurerAdapter.Just extend WebMvcConfigurerAdapter and provide the configurations that you need in this class.

Remember to add @Configuration annotation so that this class will be picked up by spring during component scan.

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

  @Autowired 
  HandlerInterceptor customInjectedInterceptor;

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

This is how you normally add interceptors to spring boot applications.Hope this might help answer your question.

From spring 5.x.x or spring-boot 2 onwards , WebMvcConfigurerAdapter is marked as deprecated.The WebMvcConfigurer interface (which is implemented by the abstract class WebMvcConfigurerAdapter), starting with Spring 5, contains default implementations for all its methods. As a result, the abstract adapter class was marked as deprecated.You can adopt it if you like as follows :

@Configuration
public WebConfig implements WebMvcConfigurer {
    // ...
}

Upvotes: 10

Related Questions