>想象一下我们有一个这样的控制器:
@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
工作的方式为这个特定的控制器实现一些常见的预\后处理? 例如,我想在控制器中有一个方法,该方法在处理程序方法之前接收请求,但只接收对该特定控制器的请求。
我知道RequestBodyAdvice
和ResponseBodyAdvice
接口,但想要一些本地控制器。
作为用法示例 - 我想在每个处理程序之前对公共parameter
变量进行一些验证。
以上所有遗漏的内容回答了如何为特定控制器注册拦截器,可以按如下方式完成:
@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>
春季文档
你需要写你自己的HandlerInterceptor
。您可以通过扩展HandlerInterceptorAdapter
轻松做到这一点。然后你可以覆盖preHandle()
和/或postHandle()
.
preHandle()
在确定适当的HandlerMapping
处理程序对象,但在HandlerAdapter
调用处理程序之前。
postHandle()
是在实际调用HandlerAdapter
之后调用的 处理程序,但在DispatcherServlet
呈现视图之前。
您可以使用 HttpServletRequest
的 getRequestURI()
方法为 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 参考的 22.16.5 拦截器部分。
使用 HandlerInterceptorAdapter
截取控制器执行前后,记录执行时间的开始和结束时间,保存到现有截获控制器的模型中,以备后用。
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/
由于您希望以通用方式处理路径变量,请考虑引入模型对象。有了这个,您可以验证属性(java bean 验证(,还可以混合路径变量和查询参数(这里有一个非常简单的示例,您甚至可以创建自定义验证(:
@Data
class SomeModel {
@NotEmpty
private String parameter;
}
在控制器中,您只需将模型添加为参数:
@RequestMapping("/something")
public Object handleSomething(@Valid SomeModel model) {
/* handle using model.getParameter() */
}
虽然HandlerInterceptorAdapter
似乎是"正确"的解决方案,它似乎不是您想要的解决方案。
下面的代码可能是您想要的解决方案(或者至少是您在问题中要求的那个(。
摘要:编写自己的preBlam
和postBlam
方法。
一些代码:
@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用户,所以我不能只是举一个例子。