Java Spring MVC@Controller拦截自己的请求
假设我们有这样一个控制器:Java Spring MVC@Controller拦截自己的请求,java,spring,spring-mvc,spring-restcontroller,Java,Spring,Spring Mvc,Spring Restcontroller,假设我们有这样一个控制器: @RestController @RequestMapping("/{parameter}") public class MyController { @ExceptionHandler(SomeException.class) public Object handleSomeException() { /* handle */ } @RequestMapping("/something") public Object handle
@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接口,但希望控制器具有本地功能
作为一个使用示例,我想在每个处理程序之前对公共参数变量进行一些验证。使用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");
}
}
更多示例-使用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");
}
}
还有一些例子-你需要自己写。通过扩展,您可以很容易地做到这一点。然后可以覆盖预处理和/或后处理 在HandlerMapping确定适当的 handler对象,但在HandlerAdapter调用该处理程序之前 postHandle是在HandlerAdapter实际调用 处理程序,但在DispatcherServlet呈现视图之前 您可以使用HttpServletRequest的getRequestURI方法为预处理中的不同处理程序添加逻辑 例如:
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
您可以将其配置为更特定于url。请参阅Spring参考的一节 你需要自己写。通过扩展,您可以很容易地做到这一点。然后可以覆盖预处理和/或后处理 在HandlerMapping确定适当的 handler对象,但在HandlerAdapter调用该处理程序之前 postHandle是在HandlerAdapter实际调用 处理程序,但在DispatcherServlet呈现视图之前 您可以使用HttpServletRequest的getRequestURI方法为预处理中的不同处理程序添加逻辑 例如:
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
您可以将其配置为更特定于url。请参阅Spring参考的一节 虽然HandlerInterceptorAdapter似乎是正确的解决方案,
这似乎不是您想要的解决方案
下面的代码可能就是您想要的解决方案
或者至少是你在问题中要求的
总结:编写您自己的preBlam和postbrum方法
一些代码:
@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用户,所以我不能随便举个例子。虽然HandlerInterceptorAdapter似乎是正确的解决方案,
这似乎不是您想要的解决方案
下面的代码可能就是您想要的解决方案
或者至少是你在问题中要求的
总结:编写您自己的preBlam和postbrum方法
一些代码:
@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用户,所以我不能只是喋喋不休地讲一个例子。如果您想以一种通用的方式处理path变量,请考虑引入一个模型对象。有了它,您可以验证属性java bean validation,还可以混合路径变量和查询参数。这里有一个非常简单的示例,您甚至可以创建自定义验证:
@Data
class SomeModel {
@NotEmpty
private String parameter;
}
在控制器中,您只需添加模型作为参数:
@RequestMapping("/something")
public Object handleSomething(@Valid SomeModel model) {
/* handle using model.getParameter() */
}
由于希望以通用方式处理path变量,请考虑引入一个模型对象。有了它,您可以验证属性java bean validation,还可以混合路径变量和查询参数。这里有一个非常简单的示例,您甚至可以创建自定义验证:
@Data
class SomeModel {
@NotEmpty
private String parameter;
}
在控制器中,您只需添加模型作为参数:
@RequestMapping("/something")
public Object handleSomething(@Valid SomeModel model) {
/* handle using model.getParameter() */
}
以上所有问题中遗漏的是如何为特定控制器注册拦截器,具体操作如下:
@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>
以上所有问题中遗漏的是如何为特定控制器注册拦截器,具体操作如下:
@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的AOP:您也可以使用这里提到的HandlerInterceptor查看spring的AOP:您也可以使用这里提到的HandlerInterceptor