RESTful API的拦截

作者: 我可能是个假开发 | 来源:发表于2018-04-28 12:31 被阅读131次

    RESTful API的拦截

    场景:对RESTfulAPI作统一的处理,比如希望对所有的RESTfulAPI记录服务处理的时间。

    一、过滤器Filter

    可以拿到原始的http请求和响应的信息,
    但是拿不到真正处理请求的那个方法的信息。

    自定义TimeFilter:

    @Component
    public class TimeFilter implements Filter{
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("time filter init");
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            System.out.println("time filter start");
            long start = new Date().getTime();
            chain.doFilter(request, response);
            System.out.println("time filter耗时: "+(new Date().getTime()-start));
            System.out.println("time filter finish");
        }
    
        @Override
        public void destroy() {
            System.out.println("time filter destroy");
        }
    
    }
    

    控制台打印:

    time filter init
    time filter start
    进入getInfo服务
    time filter 耗时:0
    time filter finish
    

    在使用第三方过滤器时,需要通过web.xml配置;但对springboot来说,没有该配置文件

    使用配置类:

    @Configuration
    public class WebConfig {
        
        @Bean
        public FilterRegistrationBean timeFiler() {
            FilterRegistrationBean registrationBean = new FilterRegistrationBean();
            
            TimeFilter timeFilter = new TimeFilter();
            
            registrationBean.setFilter(timeFilter);
            
            List<String> urls = new ArrayList<>();
            
            urls.add("/*");//所有路径都起作用
            registrationBean.setUrlPatterns(urls);
            return registrationBean;
        }
        
    }
    

    二、拦截器Interceptor

    既可以拿到原始的http请求和响应,
    也可以拿到真正处理请求的方法的信息,
    但是拿不到具体方法被调用的时候的调用的参数的值。

    使用Interceptor记录服务耗时

    TimeInterceptor:

    package com.hcx.web.interceptor;
    
    import java.util.Date;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.stereotype.Component;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    @Component
    public class TimeInterceptor implements HandlerInterceptor{
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            System.out.println("preHandle");
            
            System.out.println(((HandlerMethod)handler).getBean().getClass().getName());
            System.out.println(((HandlerMethod)handler).getMethod().getName());
            
            request.setAttribute("startTime", new Date().getTime());
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            System.out.println("postHandle");
            Long start = (Long) request.getAttribute("startTime");
            System.out.println("time interceptor耗时:"+(new Date().getTime()-start));
        }
    
        //不管控制器方法成功还是失败,都会进入该方法
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            System.out.println("afterCompletion");
            Long start = (Long) request.getAttribute("startTime");
            System.out.println("time interceptor耗时:"+(new Date().getTime()-start));
            System.out.println("ex is "+ex);
        }
    
    }
    

    配置:

    @Configuration
    public class WebConfig extends WebMvcConfigurerAdapter{
        
        @Autowired
        private TimeInterceptor timeInterceptor;
        
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(timeInterceptor);
        }
        
        @Bean
        public FilterRegistrationBean timeFiler() {
            FilterRegistrationBean registrationBean = new FilterRegistrationBean();
            
            TimeFilter timeFilter = new TimeFilter();
            
            registrationBean.setFilter(timeFilter);
            
            List<String> urls = new ArrayList<>();
            
            urls.add("/*");
            registrationBean.setUrlPatterns(urls);
            return registrationBean;
        }
        
    }
    

    运行结果:

    time filter start
    preHandle
    com.hcx.web.controller.UserController$$EnhancerBySpringCGLIB$$ab2fe549
    getInfo
    进入getInfo服务
    postHandle
    time interceptor耗时:2
    afterCompletion
    time interceptor耗时:2
    ex is null
    time filter耗时: 4
    time filter finish
    

    如果方法抛出异常:

    @GetMapping("/{id:\\d+}")
        @JsonView(User.UserDetailView.class)
        public User getInfo(@PathVariable String id) {
            throw new UserNotExistException(id);
    //      System.out.println("进入getInfo服务");
    //      User user = new User();
    //      user.setUsername("tom");
    //      return user;
        }
    

    运行结果:没有了posthandler,ex在afterCompletion为空是因为异常处理器ControllerExceptionHandler把抛出去的异常处理了,没有传递到方法里。(即ControllerExceptionHandler该控制器是在afterCompletion之前执行的),除非抛出的异常没有被该异常处理器处理,比如runtimeException。

    time filter init
    time filter start
    preHandle
    com.hcx.web.controller.UserController$$EnhancerBySpringCGLIB$$68887b2a
    getInfo
    afterCompletion
    time interceptor耗时:40
    ex is null
    time filter耗时: 49
    time filter finish
    

    局限:无法知道方法中的具体参数

    三、切片Aspect

    可以拿到方法被调用的时候传递进来的参数的值,
    但是拿不到原始的http请求和响应的对象。

    springaop.png

    第一步:需要添加依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    

    第二步:编写切面类

    @Aspect
    @Component
    public class TimeAspect {
        
        @Around("execution(* com.hcx.web.controller.UserController.*(..))")
        public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("time aspect start");
            
            //方法参数的数组
            Object[] args = pjp.getArgs();
            for (Object arg : args) {
                System.out.println("arg is "+arg);
            }
            
            long start = new Date().getTime();
            
            Object object = pjp.proceed();
            System.out.println("time aspect 耗时:"+(new Date().getTime() - start));
            
            System.out.println("time aspect end");
            return object;
        }
        
    }
    

    控制台打印:

    time filter start
    preHandle
    com.hcx.web.controller.UserController$$EnhancerBySpringCGLIB$$ab2fe549
    getInfo
    time aspect start
    arg is 1
    进入getInfo服务
    time aspect 耗时:0
    time aspect end
    postHandle
    time interceptor耗时:2
    afterCompletion
    time interceptor耗时:2
    ex is null
    time filter耗时: 4
    time filter finish
    
    拦截执行顺序.png

    相关文章

      网友评论

      • 测试大头兵:文主请允许我小小广告一下 测试入门到大神 QQ群 755431660 爱学习,爱进步的测试猿们,欢迎添加哦~

      本文标题:RESTful API的拦截

      本文链接:https://www.haomeiwen.com/subject/gihglftx.html