【问题标题】:Spring MVC @Controller to intercept own requestsSpring MVC @Controller 拦截自己的请求
【发布时间】:2017-03-29 15:31:32
【问题描述】:

假设我们有一个这样的控制器:

@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 变量进行一些验证。

【问题讨论】:

标签: java spring spring-mvc spring-restcontroller


【解决方案1】:

以上所有问题都回答了如何为特定控制器注册拦截器,可以如下完成:

@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

【讨论】:

    【解决方案2】:

    您需要自己写HandlerInterceptor。您可以通过扩展HandlerInterceptorAdapter 轻松做到这一点。然后你可以覆盖preHandle() 和/或postHandle()

    preHandle()HandlerMapping 确定合适的后调用 处理程序对象,但在HandlerAdapter 调用处理程序之前。

    postHandle()HandlerAdapter 实际调用 处理程序,但在 DispatcherServlet 呈现视图之前。

    您可以使用HttpServletRequestgetRequestURI() 方法为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;
        }
    }
    

    然后在您的dispatcher-servlet.xml 中注册这个HandlerInterceptor

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

    您可以将其配置为更加特定于 url。请参阅 Spring Reference 的 22.16.5 Interceptors 部分。

    【讨论】:

      【解决方案3】:

      使用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/

      【讨论】:

        【解决方案4】:

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

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

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

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

        【讨论】:

          【解决方案5】:

          虽然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 为受影响的方法设置 pre 和 post 处理程序。 我不是 AOP 的大用户,所以我不能随便举个例子。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-03-17
            • 2013-03-17
            • 2019-02-04
            • 1970-01-01
            • 2014-05-19
            • 2014-08-27
            • 1970-01-01
            • 2018-07-11
            相关资源
            最近更新 更多