spring-boot 攔截

過濾器

Filter過濾器并不是標準的Servlet,它只是對Web容器和Servlet之間的過濾器。主要是對ServletRequest預(yù)處理和ServletResponse的后處理。

graph LR
webBrowser[web瀏覽器] --請求--> webContainer[web容器]
webContainer --請求--> filter[過濾器]
filter --請求--> servlet
servlet --響應(yīng)--> filter
filter --響應(yīng)--> webContainer
webContainer --響應(yīng)--> webBrowser

springboot實現(xiàn)

//編寫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("/*");
        //設(shè)置順序
        registration.setOrder(10);
        return registration;
    }

    @Bean
    public FilterRegistrationBean filter2(){
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new Filter2());
        //過濾器名稱
        registration.setName("filter2");
        //攔截路徑
        registration.addUrlPatterns("/*");
        //設(shè)置順序
        registration.setOrder(1);
        return registration;
    }
}
@GetMapping("/say")
public String hello() {
    System.err.println("say hello");
    return "hello  boot";
}

執(zhí)行結(jié)果

result.png

查看發(fā)現(xiàn)過濾器已經(jīng)生效了,spring-boot使用 order設(shè)置過濾器的執(zhí)行順序,從小到大。

備注

Servlet3.0增加了通過@WebFilter注冊過濾器。但是使用注解方式實現(xiàn)無法配置執(zhí)行順序。

攔截器

過濾器為Servlet內(nèi)的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) {
        //注冊攔截器 攔截規(guī)則
        //多個攔截器時 以此添加 執(zhí)行順序按添加順序
        registry.addInterceptor(interceptor1()).addPathPatterns("/**").order(2);
        registry.addInterceptor(interceptor2()).addPathPatterns("/**").order(1);
    }
}

執(zhí)行結(jié)果

result.png

order設(shè)置執(zhí)行順序,從小到大。

RequestBodyAdvice/ResponseBodyAdvice

RequestBodyAdvice可攔截@RequestBody請求體參數(shù)的方法。

ResponseBodyAdvice對于返回內(nèi)容做攔截處理。

@RestControllerAdvice
public class RequestAdvice implements RequestBodyAdvice {

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

    //在請求體未讀取(轉(zhuǎn)換)時調(diào)用
    @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);
    }

    //在請求體完成讀取后調(diào)用
    @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;
    }

    //當請求體為空時調(diào)用
    @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;
    }
}

返回結(jié)果

result.png

備注

使用情況很少,場景多是對請求參數(shù)加密,返回結(jié)果加密。

區(qū)別

result.png
  1. 過濾器在攔截器之前執(zhí)行。
  2. 過濾器是Servlet規(guī)范,所以攔截器只可用于web程序,攔截器是Spring規(guī)范,所以攔截器內(nèi)可注Spring對象。
  3. 攔截器比過濾器更加精細,可在請求前后,視圖渲染后攔截,而過濾器只可在Servlet前后。
  4. RequestBodyAdvice只可攔截參數(shù)注解@RequestBody的post請求。
  5. 攔截器,過濾器中使用HttpServletRequst 獲取body中的數(shù)據(jù)時只能使用輸入流讀取,但是輸入流只能讀取一次,所以需要將HttpServletRequest再次包裝緩存body內(nèi)容。Advice則可多次讀取。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容