scorpp
scorpp

Reputation: 648

Spring MVC @Controller to intercept own requests

Imagine we have a controller like this:

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

    @ExceptionHandler(SomeException.class)
    public Object handleSomeException() { /* handle */ }

    @RequestMapping("/something")
    public Object handleSomething(@PathVariable("parameter") String parameter) {
        /* handle */
    }

    @RequestMapping("/somethingElse")
    public Object handleSomethingElse(@PathVariable("parameter") String parameter) {
        /* handle */
    }
}

The question is, how to implement some common pre-\post-handling for this particular controller in similar way as @ExceptionHandler is working? E.g. I want to have a method in controller which receives request prior to handler methods, but only requests for this particular controller.

I'm aware of RequestBodyAdvice and ResponseBodyAdvice interfaces, but want something local to controller.

As a usage example - I want to do some validation for common parameter variable before each handler.

Upvotes: 4

Views: 8515

Answers (5)

Sylwek
Sylwek

Reputation: 685

What is miss in all above answers it how to register interceptor for specific controller, which can be done as follows:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LocaleChangeInterceptor());
        registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
        registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
    }
}

In XML, the same:

<mvc:interceptors>
    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/admin/**"/>
        <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/secure/*"/>
        <bean class="org.example.SecurityInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

spring documentation

Upvotes: 8

DwB
DwB

Reputation: 38300

While HandlerInterceptorAdapter appears to be the "correct" solution, it does not appear to be the solution you want.

The code below might be the solution you want (or at least the one you asked for in your question).

Summary: write your own preBlam and postBlam methods.

Some code:

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

    @ExceptionHandler(SomeException.class)
    public Object handleSomeException()
    {
    /* handle */
    }

    @RequestMapping("/something")
    public Object handleSomething(@PathVariable("parameter") String parameter)
    {
        preBlam(desired params here);

        /* handle */

        postBlam(desired params here);
    }

    @RequestMapping("/somethingElse")
    public Object handleSomethingElse(@PathVariable("parameter") String parameter)
    {
        preBlam(desired params here);

        /* handle */

        postBlam(desired params here);
    }

    private blam preBlam(parameters)
    {
    // do initial blamish work
    }

    private blam postBlam(parameters)
    {
    // do post blamish work here
    }
}

Another option: Use AOP to set pre and post handlers for the impacted methods. I'm not a big AOP user, so I can't just rattle off an example.

Upvotes: 0

Arne Burmeister
Arne Burmeister

Reputation: 20594

As you want to handle the path variable in a common way, think about introducing a model object. With this you can validate the attribute (java bean validation) and also mix path variables and query parameters (a very simple example here, you can even create custom validations):

@Data
class SomeModel {
  @NotEmpty
  private String parameter;
}

In the controller you simply have to add the model as a parameter:

@RequestMapping("/something")
public Object handleSomething(@Valid SomeModel model) {
  /* handle using model.getParameter() */
}

Upvotes: 0

Minar Mahmud
Minar Mahmud

Reputation: 2665

You will need to write you own HandlerInterceptor. You can do it easily by extending HandlerInterceptorAdapter. And then you can override preHandle() and/or postHandle().

preHandle() is called after HandlerMapping determined an appropriate handler object, but before HandlerAdapter invokes the handler.

postHandle() is called after HandlerAdapter actually invoked the handler, but before the DispatcherServlet renders the view.

You can use the getRequestURI() method of HttpServletRequest to add logics for different handlers in preHandle().

Example:

public class ValidationInterceptor extends HandlerInterceptorAdapter {

    public static final String FOO_URL = "foo";
    public static final String BAR_URL = "bar";

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

        String uri = request.getRequestURI();

        if (FOO_URL.equals(uri)) {        
            // for example - validation failed
            response.sendRedirect("/to/some/url");
            return false;
        } else if (BAR_URL.equals(uri)) {
            // for example - validation successful
        }
        return true;
    }
}

Then register this HandlerInterceptor in your dispatcher-servlet.xml.

<mvc:interceptors>
    <bean class="your.package.ValidationInterceptor"/>
</mvc:interceptors>

You can configure this to be more url-specific. See 22.16.5 Interceptors section of Spring Reference.

Upvotes: 2

Stepan
Stepan

Reputation: 41

Use HandlerInterceptorAdapter

Intercept the before and after controller execution, log the start and end of the execution time, save it into the existing intercepted controller’s modelAndView for later display.

public class ExecuteTimeInterceptor extends HandlerInterceptorAdapter{

private static final Logger logger = Logger.getLogger(ExecuteTimeInterceptor.class);

//before the actual handler will be executed
public boolean preHandle(HttpServletRequest request,
    HttpServletResponse response, Object handler)
    throws Exception {

    long startTime = System.currentTimeMillis();
    request.setAttribute("startTime", startTime);

    return true;
}

//after the handler is executed
public void postHandle(
    HttpServletRequest request, HttpServletResponse response,
    Object handler, ModelAndView modelAndView)
    throws Exception {

    long startTime = (Long)request.getAttribute("startTime");

    long endTime = System.currentTimeMillis();

    long executeTime = endTime - startTime;

    //modified the exisitng modelAndView
    modelAndView.addObject("executeTime",executeTime);

    //log it
    if(logger.isDebugEnabled()){
       logger.debug("[" + handler + "] executeTime : " + executeTime + "ms");
    }
}

Some more examples - http://www.mkyong.com/spring-mvc/spring-mvc-handler-interceptors-example/

Upvotes: 0

Related Questions