5

过滤器、拦截器入门,看这一篇就够了

 3 years ago
source link: https://segmentfault.com/a/1190000040334248
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

过滤器、拦截器入门,看这一篇就够了

实际项目开发过程中,肯定有这样的需求:

  1. 记录每个rest请求耗费的时间,入参,出参
  2. 某些请求需要判断是否有权限,某些请求是不需要权限也可以运行的

这个需求太普遍了。今天我们就一起来学习这个需求的两种实现方式:

话不多说,咱们上代码,新建日志拦截器:

@Component
public class LogInterceptor implements HandlerInterceptor {
    //在请求rest接口之前调用
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("请求url:"+request.getRequestURL());
        Map<String, String[]> parameterMap = request.getParameterMap();
        Enumeration<String> parameterNames = request.getParameterNames();

        System.out.println("请求参数:");
        while (parameterNames.hasMoreElements()) {
            String name = (String) parameterNames.nextElement();
            String value = request.getParameter(name);
            System.out.println(name+"==>"+value);
        }

        //假设参数a传递的如果是1的话,是不合法的请求,不继续往下执行
        if("1".equals(request.getParameter("a"))){
            return false;
        }
        return true;//如果返回false则不会继续往下执行,不会真正的进入到controller
    }

    //请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle is called...");
    }

    //在整个请求结束之后被调用
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion is called...");
    }
}

WebMvcConfigurer中注册该拦截器,并告诉spring哪些路径需要拦截,哪些路径不需要拦截

@Configuration
public class InterceperConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //添加拦截器
        InterceptorRegistration interceptorRegistration = registry.addInterceptor(new LogInterceptor() {
        });
        //如下的路径不拦截,多个路径用逗号分隔
        interceptorRegistration.excludePathPatterns("/demo/test2");
        //拦截如下路径
        interceptorRegistration.addPathPatterns("/**");
    }
}

好了,拦截器就做完了,so easy。下面我们写个controller来测试下吧!

@RestController
@RequestMapping("/demo")
public class DemoController {

    @GetMapping("test1")
    public String test1(){
        System.out.println("rest.test1 is called");
        return "test1";
    }

    @GetMapping("test2")
    public String test2(){
        System.out.println("rest.test2 is called");
        return "test2";
    }

    @GetMapping("test3")
    public String test3(@RequestParam("a")String a){
        System.out.println("rest.test3 is called");
        return "test3";
    }
}

测试的结果基本可以预期到

  1. demo/test1 和 demo/test3会被拦截,进拦截器
  2. demo/test2 不会进拦截器
  3. demo/test3?a=1 这个请求会进拦截器,但是进不了controller。
@WebFilter(urlPatterns = "/*", filterName = "LogFilter")
public class LogFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("logfilter.init is called...");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filter中打印的请求参数:");
        Enumeration<String> parameterNames = servletRequest.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String key = parameterNames.nextElement();
            String value = servletRequest.getParameter(key);
            System.out.println(key+"==>"+value);
        }

        long bgn = System.currentTimeMillis();
        //一定要调用链式调用传递,否则也进不了controller
        filterChain.doFilter(servletRequest,servletResponse);
        long end = System.currentTimeMillis();
        System.out.println("filter中记录耗时:"+(end-bgn)+"ms");
    }

    @Override
    public void destroy() {
        System.out.println("logfilter.destroy is called...");
    }
}

注意:@WebFilter这个注解是Servlet3.0的规范,并不是Spring boot提供的。除了这个注解以外,我们还需在配置类中加另外一个注解:@ServletComponetScan,指定扫描的包。

拦截器、过滤器执行顺序,默认先执行过滤器,再执行拦截器。

如果过滤器有多个会根据过滤器的名词按照A-Z的排序先后执行。这是因为@WebFilter这个注解不支持执行顺序。当然你也可以通过写一个配置文件解决。

@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean reqResFilter1() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        LogFilter logFilter = new LogFilter();
        filterRegistrationBean.setFilter(logFilter);
        filterRegistrationBean.addUrlPatterns("/demo/test1","/demo/test3");//配置过滤规则
        filterRegistrationBean.addInitParameter("name","spingboot");//设置init参数
        filterRegistrationBean.setName("logFilter");//设置过滤器名称
        filterRegistrationBean.setOrder(2);//执行次序

        return filterRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean reqResFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        Log1Filter log1Filter = new Log1Filter();
        filterRegistrationBean.setFilter(log1Filter);
        filterRegistrationBean.addUrlPatterns("*");//配置过滤规则
        filterRegistrationBean.setName("Log1Filter");//设置过滤器名称
        filterRegistrationBean.setOrder(1);//执行次序
        
        return filterRegistrationBean;
    }
}

更多java原创阅读:https://javawu.com/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK