Restful API 的拦截
在某些情况下,会有需求对api拦截做一些统一的业务处理。简单的如api执行时间等。下面用过滤器、拦截器、切片三种方式实现统计api执行时间功能,从中介绍三者的区别。
过滤器(Filter)实现:
@Component
public class TimeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//开始时间
long start = System.currentTimeMillis();
//执行下一个过滤器。无执行Api
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("执行时间为:"+(System.currentTimeMillis()-start));
}
@Override
public void destroy() {
}
}
有时我们需要使用第三方的过滤器,在第三方的过滤器中没有@Component注解,也就是不能自动注册成bean。在springboot项目中可通过配置注解解决问题。如下:
@Configuration
public class WebConfig{
@Bean
public FilterRegistrationBean timeFilter(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
TimeFilter timeFilter = new TimeFilter();
filterRegistrationBean.setFilter(timeFilter);
List<String> urls = new ArrayList<>(4);
urls.add("/*");
filterRegistrationBean.setUrlPatterns(urls);
return filterRegistrationBean;
}
}
Filter只能拦截servlet请求响应,不能知道是那个控制器执行的那个API。若要获取可使用Interceptor拦截器。
拦截器(Interceptor)实现
拦截器为spring框架本身提供。
@Component
public class TimeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("pre");
System.out.println(((HandlerMethod)o).getBean().getClass().getName());
System.out.println(((HandlerMethod)o).getMethod().getName());
httpServletRequest.setAttribute("startTime",System.currentTimeMillis());
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
long startTime = (long) httpServletRequest.getAttribute("startTime");
System.out.println("耗时:"+(System.currentTimeMillis()-startTime));
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("afterCompletion");
long startTime = (long) httpServletRequest.getAttribute("startTime");
System.out.println("耗时:"+(System.currentTimeMillis()-startTime));
System.out.println("ex is "+e);
}
}
在执行控制器方法前执行拦截器preHandle方法,然后执行控制器方法,控制器方法执行完毕后执行postHandle方法。最后执行afterCompletion方法。若控制器方法抛错则不会执行postHandle方法,但会执行afterCompletion方法。在方法的最后一个object类型参数中可获取控制器api信息。
和filter不同,在注册成bean后。拦截器需要额外的配置生效,如下:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor);
}
}
拦截器的缺陷在于无法拿到控制器方法中参数的值,若要获取到,可使用切片。
切片(Aspect)实现
@Aspect
@Component
public class TimeAspect {
@Around("execution(* com.imooc.web.controller.UserController.*(..))")
//表示拦UserController下所有方法
public Object handlerControllerMethod(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("切片");
//获取方法参数
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println("参数:"+arg);
}
long start = System.currentTimeMillis();
Object o = joinPoint.proceed();
System.out.println("耗时:" + (System.currentTimeMillis() - start));
return o;
}
}
总结
过滤器能获取原始的http请求和响应的消息,但不能拿到真正处理请求方法的信息。
拦截器能获取原始的http请求和响应的消息,也能拿到真正处理请求方法的信息,但不能拿到方法的参数。
切片可以拿到方法的参数,但不能拿到原始的http请求和响应的消息。
三种拦截的顺序:
filter>interceptor>controllerAdvice>aspect>controller方法
当controller方法方法抛出错误时,获取错误的顺序:
aspect>controllerAdvice>interceptor>filter
网友评论