简体   繁体   English

Spring MVC @Controller拦截自己的请求

[英]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? 问题是,如何以类似于@ExceptionHandler工作的方式为此特定控制器实现一些常见的pre \\\\ post-handling处理? Eg 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. 我知道RequestBodyAdviceResponseBodyAdvice接口,但是想要控制器本地的东西。

As a usage example - I want to do some validation for common parameter variable before each handler. 作为使用示例-我想在每个处理程序之前对公用parameter变量进行一些验证。

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: 在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 春季文件

You will need to write you own HandlerInterceptor . 您将需要编写自己的HandlerInterceptor You can do it easily by extending HandlerInterceptorAdapter . 您可以通过扩展HandlerInterceptorAdapter轻松地做到这一点。 And then you can override preHandle() and/or postHandle() . 然后,您可以覆盖preHandle()和/或postHandle()

preHandle() is called after HandlerMapping determined an appropriate handler object, but before HandlerAdapter invokes the handler. HandlerMapping确定适当的处理程序对象之后,但在HandlerAdapter调用处理程序之前,将调用preHandle()

postHandle() is called after HandlerAdapter actually invoked the handler, but before the DispatcherServlet renders the view. HandlerAdapter实际调用处理程序之后,但在DispatcherServlet呈现视图之前,将调用postHandle()

You can use the getRequestURI() method of HttpServletRequest to add logics for different handlers in preHandle() . 您可以使用HttpServletRequestgetRequestURI()方法为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 . 然后在您的dispatcher-servlet.xml注册这个HandlerInterceptor

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

You can configure this to be more url-specific. 您可以将其配置为更多特定于URL。 See 22.16.5 Interceptors section of Spring Reference. 参见Spring Reference的22.16.5拦截器部分。

While HandlerInterceptorAdapter appears to be the "correct" solution, it does not appear to be the solution you want. 尽管HandlerInterceptorAdapter似乎是“正确”的解决方案,但它似乎并不是您想要的解决方案。

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. 摘要:编写您自己的preBlampostBlam方法。

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. 另一个选择:使用AOP为受影响的方法设置前置和后置处理程序。 I'm not a big AOP user, so I can't just rattle off an example. 我不是AOP的大用户,所以我不能只是举个例子。

Use HandlerInterceptorAdapter 使用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. 截获控制器执行之前和之后,记录执行时间的开始和结束,并将其保存到现有截获的控制器的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");
    }
}

Some more examples - http://www.mkyong.com/spring-mvc/spring-mvc-handler-interceptors-example/ 其他一些示例-http: //www.mkyong.com/spring-mvc/spring-mvc-handler-interceptors-example/

As you want to handle the path variable in a common way, think about introducing a model object. 当您想以通用方式处理path变量时,请考虑引入模型对象。 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): 有了它,您可以验证属性(java bean验证),还可以混合路径变量和查询参数(这里是一个非常简单的示例,您甚至可以创建自定义验证):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM