Spring MVC @Controller拦截自己的请求

时间:2017-03-29 15:31:33

标签: java spring spring-mvc spring-restcontroller

想象一下,我们有一个像这样的控制器:

@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 */
    }
}

问题是,如何以与@ExceptionHandler类似的方式为此特定控制器实现一些常见的预处理?例如。我想在控制器中有一个方法,它在处理程序方法之前接收请求,但只请求这个特定的控制器。

我知道RequestBodyAdviceResponseBodyAdvice接口,但想要控制器本地的东西。

作为一个用法示例 - 我希望在每个处理程序之前对常见的parameter变量进行一些验证。

5 个答案:

答案 0 :(得分:2)

以上所有内容中缺少的内容回答了如何为特定控制器注册拦截器,可以通过以下操作完成:

@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/*");
    }
}

在XML中,相同:

<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

答案 1 :(得分:1)

您需要自己编写HandlerInterceptor。您可以通过扩展HandlerInterceptorAdapter轻松完成。然后,您可以覆盖preHandle()和/或postHandle()

  在preHandle()确定合适后,

HandlerMapping 会被调用   handler对象,但在HandlerAdapter调用处理程序之前。

     在postHandle()实际调用之后调用

HandlerAdapter   处理程序,但在DispatcherServlet呈现视图之前。

您可以使用getRequestURI()的{​​{1}}方法为HttpServletRequest中的不同处理程序添加逻辑。

示例:

preHandle()

然后在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; } }

中注册此HandlerInterceptor
dispatcher-servlet.xml

您可以将其配置为更具体网址。请参阅Spring Reference的22.16.5 Interceptors部分。

答案 2 :(得分:0)

使用HandlerInterceptorAdapter

拦截控制器执行前后,记录执行时间的开始和结束,将其保存到现有拦截控制器的modelAndView中以供日后显示。

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");
    }
}

更多示例 - http://www.mkyong.com/spring-mvc/spring-mvc-handler-interceptors-example/

答案 3 :(得分:0)

虽然HandlerInterceptorAdapter似乎是“正确”的解决方案, 它似乎不是你想要的解决方案。

以下代码可能是您想要的解决方案 (或者至少是你在问题中要求的那个)。

摘要:编写您自己的preBlampostBlam方法。

一些代码:

@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
    }
}

另一种选择: 使用AOP为受影响的方法设置前处理程序和后处理程序。 我不是一个很大的AOP用户,所以我不能只是滔滔不绝地说出一个例子。

答案 4 :(得分:0)

由于您希望以常用方式处理路径变量,请考虑引入模型对象。有了这个,您可以验证属性(java bean验证),还可以混合路径变量和查询参数(这里是一个非常简单的示例,您甚至可以创建自定义验证):

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

在控制器中,您只需将模型添加为参数:

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