Restful API 的攔截
在某些情況下愉镰,會(huì)有需求對(duì)api攔截做一些統(tǒng)一的業(yè)務(wù)處理氢哮。簡(jiǎn)單的如api執(zhí)行時(shí)間等葛圃。下面用過濾器默垄、攔截器此虑、切片三種方式實(shí)現(xiàn)統(tǒng)計(jì)api執(zhí)行時(shí)間功能甚纲,從中介紹三者的區(qū)別口锭。
過濾器(Filter)實(shí)現(xiàn):
@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 {
? ? ?? //開始時(shí)間
? ? ?? long start = System.currentTimeMillis();
? ? ?? //執(zhí)行下一個(gè)過濾器。無執(zhí)行Api
? ? ?? filterChain.doFilter(servletRequest, servletResponse);
? ? ?? System.out.println("執(zhí)行時(shí)間為:"+(System.currentTimeMillis()-start));
?? }
?? @Override
?? public void destroy() {
?? }
}
?
有時(shí)我們需要使用第三方的過濾器,在第三方的過濾器中沒有@Component注解鹃操,也就是不能自動(dòng)注冊(cè)成bean韭寸。在springboot項(xiàng)目中可通過配置注解解決問題。如下:
@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請(qǐng)求響應(yīng)荆隘,不能知道是那個(gè)控制器執(zhí)行的那個(gè)API恩伺。若要獲取可使用Interceptor攔截器。
攔截器(Interceptor)實(shí)現(xiàn)
攔截器為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("耗時(shí):"+(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("耗時(shí):"+(System.currentTimeMillis()-startTime));
? ? ?? System.out.println("ex is "+e);
?? }
}
在執(zhí)行控制器方法前執(zhí)行攔截器preHandle方法晶渠,然后執(zhí)行控制器方法,控制器方法執(zhí)行完畢后執(zhí)行postHandle方法燃观。最后執(zhí)行afterCompletion方法褒脯。若控制器方法拋錯(cuò)則不會(huì)執(zhí)行postHandle方法,但會(huì)執(zhí)行afterCompletion方法缆毁。在方法的最后一個(gè)object類型參數(shù)中可獲取控制器api信息番川。
和filter不同,在注冊(cè)成bean后脊框。攔截器需要額外的配置生效颁督,如下:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
?? @Autowired
?? private TimeInterceptor timeInterceptor;
?? @Override
?? public void addInterceptors(InterceptorRegistry registry) {
? ? ?? registry.addInterceptor(timeInterceptor);
?? }
}
攔截器的缺陷在于無法拿到控制器方法中參數(shù)的值,若要獲取到浇雹,可使用切片沉御。
切片(Aspect)實(shí)現(xiàn)
@Aspect
@Component
public class TimeAspect {
?? @Around("execution(* com.imooc.web.controller.UserController.*(..))")
? ? ? ?? //表示攔UserController下所有方法
?? public Object handlerControllerMethod(ProceedingJoinPoint joinPoint) throws Throwable {
? ? ?? System.out.println("切片");
? ? ?? //獲取方法參數(shù)
? ? ?? Object[] args = joinPoint.getArgs();
? ? ?? for (Object arg : args) {
? ? ? ? ?? System.out.println("參數(shù):"+arg);
? ? ?? }
? ? ?? long start = System.currentTimeMillis();
? ? ?? Object o = joinPoint.proceed();
? ? ?? System.out.println("耗時(shí):" + (System.currentTimeMillis() - start));
? ? ?? return o;
?? }
}
總結(jié)
過濾器能獲取原始的http請(qǐng)求和響應(yīng)的消息,但不能拿到真正處理請(qǐng)求方法的信息昭灵。
攔截器能獲取原始的http請(qǐng)求和響應(yīng)的消息嚷节,也能拿到真正處理請(qǐng)求方法的信息,但不能拿到方法的參數(shù)虎锚。
切片可以拿到方法的參數(shù)硫痰,但不能拿到原始的http請(qǐng)求和響應(yīng)的消息。
三種攔截的順序:
filter>interceptor>controllerAdvice>aspect>controller方法
當(dāng)controller方法方法拋出錯(cuò)誤時(shí)窜护,獲取錯(cuò)誤的順序:
aspect>controllerAdvice>interceptor>filter