spring-boot 拦截

过滤器

Filter过滤器并不是标准的Servlet,它只是对Web容器和Servlet之间的过滤器。主要是对ServletRequest预处理和ServletResponse的后处理。

graph LR
webBrowser[web浏览器] --请求--> webContainer[web容器]
webContainer --请求--> filter[过滤器]
filter --请求--> servlet
servlet --响应--> filter
filter --响应--> webContainer
webContainer --响应--> webBrowser

springboot实现

//编写Filter
public class Filter1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //主要是为了打印看的清楚
        System.err.println("Filter1");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        System.err.println("请求URL:" + httpServletRequest.getRequestURI());

        System.err.println("Filter1");

        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}
//编写Filter
public class Filter2 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.err.println("Filter2");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        System.err.println("请求URL:" + httpServletRequest.getRequestURI());

        System.err.println("Filter2");


        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}
//注册Filter
@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean filter1(){
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new Filter1());
        //过滤器名称
        registration.setName("filter1");
        //拦截路径
        registration.addUrlPatterns("/*");
        //设置顺序
        registration.setOrder(10);
        return registration;
    }

    @Bean
    public FilterRegistrationBean filter2(){
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new Filter2());
        //过滤器名称
        registration.setName("filter2");
        //拦截路径
        registration.addUrlPatterns("/*");
        //设置顺序
        registration.setOrder(1);
        return registration;
    }
}
@GetMapping("/say")
public String hello() {
    System.err.println("say hello");
    return "hello  boot";
}

执行结果

result.png

查看发现过滤器已经生效了,spring-boot使用 order设置过滤器的执行顺序,从小到大。

备注

Servlet3.0增加了通过@WebFilter注册过滤器。但是使用注解方式实现无法配置执行顺序。

拦截器

过滤器为Servlet内的Api。HandlerInterceptor为Spring 提供的拦截器。

//编写拦截器
public class Interceptor1 extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.err.println("Interceptor1 请求前");
        return super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.err.println("Interceptor1 请求后");
        super.postHandle(request, response, handler, modelAndView);
    }
}

public class Interceptor2 extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.err.println("Interceptor2 请求前");
        return super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.err.println("Interceptor2 请求后");
        super.postHandle(request, response, handler, modelAndView);
    }
}

//拦截器配置
//注册拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    // 直接 new 不能注入组件
    @Bean
    public Interceptor1 interceptor1(){
        return new Interceptor1();
    }

    @Bean
    public Interceptor2 interceptor2(){
        return new Interceptor2();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册拦截器 拦截规则
        //多个拦截器时 以此添加 执行顺序按添加顺序
        registry.addInterceptor(interceptor1()).addPathPatterns("/**").order(2);
        registry.addInterceptor(interceptor2()).addPathPatterns("/**").order(1);
    }
}

执行结果

result.png

order设置执行顺序,从小到大。

RequestBodyAdvice/ResponseBodyAdvice

RequestBodyAdvice可拦截@RequestBody请求体参数的方法。

ResponseBodyAdvice对于返回内容做拦截处理。

@RestControllerAdvice
public class RequestAdvice implements RequestBodyAdvice {

    //判断是否支持
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    //在请求体未读取(转换)时调用
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {

        System.err.println("RequestAdvice beforeBodyRead");

        return new TestHttpInputMessage(inputMessage);
    }

    //在请求体完成读取后调用
    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        System.err.println("RequestAdvice afterBodyRead");

        System.err.println(body);

        return body;
    }

    //当请求体为空时调用
    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        System.err.println("RequestAdvice handleEmptyBody");
        return body;
    }

    class TestHttpInputMessage implements HttpInputMessage {
        private HttpHeaders headers;
        private InputStream body;

        public TestHttpInputMessage(HttpInputMessage httpInputMessage) throws IOException {
            this.headers = httpInputMessage.getHeaders();
            this.body = httpInputMessage.getBody();
        }

        @Override
        public InputStream getBody() throws IOException {
            return body;
        }

        @Override
        public HttpHeaders getHeaders() {
            return headers;
        }
    }

}
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    //判断是否支持
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

        System.err.println("ResponseAdvice");
        System.err.println(body);

        return body;
    }
}

返回结果

result.png

备注

使用情况很少,场景多是对请求参数加密,返回结果加密。

区别

result.png
  1. 过滤器在拦截器之前执行。
  2. 过滤器是Servlet规范,所以拦截器只可用于web程序,拦截器是Spring规范,所以拦截器内可注Spring对象。
  3. 拦截器比过滤器更加精细,可在请求前后,视图渲染后拦截,而过滤器只可在Servlet前后。
  4. RequestBodyAdvice只可拦截参数注解@RequestBody的post请求。
  5. 拦截器,过滤器中使用HttpServletRequst 获取body中的数据时只能使用输入流读取,但是输入流只能读取一次,所以需要将HttpServletRequest再次包装缓存body内容。Advice则可多次读取。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容