欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot之自定義Filter獲取請(qǐng)求參數(shù)與響應(yīng)結(jié)果案例詳解

 更新時(shí)間:2021年09月03日 11:22:44   作者:沉潛飛動(dòng)  
這篇文章主要介紹了SpringBoot之自定義Filter獲取請(qǐng)求參數(shù)與響應(yīng)結(jié)果案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下

一個(gè)系統(tǒng)上線,肯定會(huì)或多或少的存在異常情況。為了更快更好的排雷,記錄請(qǐng)求參數(shù)和響應(yīng)結(jié)果是非常必要的。所以,Nginx 和 Tomcat 之類的 web 服務(wù)器,都提供了訪問日志,可以幫助我們記錄一些請(qǐng)求信息。

本文是在我們的應(yīng)用中,定義一個(gè)Filter來實(shí)現(xiàn)記錄請(qǐng)求參數(shù)和響應(yīng)結(jié)果的功能。

有一定經(jīng)驗(yàn)的都知道,如果我們?cè)?code>Filter中讀取了HttpServletRequest或者HttpServletResponse的流,就沒有辦法再次讀取了,這樣就會(huì)造成請(qǐng)求異常。所以,我們需要借助 Spring 提供的ContentCachingRequestWrapperContentCachingRequestWrapper實(shí)現(xiàn)數(shù)據(jù)流的重復(fù)讀取。

定義 Filter

通常來說,我們自定義的Filter是實(shí)現(xiàn)Filter接口,然后寫一些邏輯,但是既然是在 Spring 中,那就借助 Spring 的一些特性。在我們的實(shí)現(xiàn)中,要繼承OncePerRequestFilter實(shí)現(xiàn)我們的自定義實(shí)現(xiàn)。

從類名上推斷,OncePerRequestFilter是每次請(qǐng)求只執(zhí)行一次,但是,難道Filter在一次請(qǐng)求中還會(huì)執(zhí)行多次嗎?Spring 官方也是給出定義這個(gè)類的原因:

Filter base class that aims to guarantee a single execution per request dispatch, on any servlet container. It provides a doFilterInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain) method with HttpServletRequest and HttpServletResponse arguments.

As of Servlet 3.0, a filter may be invoked as part of a REQUEST or ASYNC dispatches that occur in separate threads. A filter can be configured in web.xml whether it should be involved in async dispatches. However, in some cases servlet containers assume different default configuration. Therefore sub-classes can override the method shouldNotFilterAsyncDispatch() to declare statically if they should indeed be invoked, once, during both types of dispatches in order to provide thread initialization, logging, security, and so on. This mechanism complements and does not replace the need to configure a filter in web.xml with dispatcher types.

Subclasses may use isAsyncDispatch(HttpServletRequest) to determine when a filter is invoked as part of an async dispatch, and use isAsyncStarted(HttpServletRequest) to determine when the request has been placed in async mode and therefore the current dispatch won't be the last one for the given request.

Yet another dispatch type that also occurs in its own thread is ERROR. Subclasses can override shouldNotFilterErrorDispatch() if they wish to declare statically if they should be invoked once during error dispatches.

也就是說,Spring 是為了兼容不同的 Web 容器,所以定義了只會(huì)執(zhí)行一次的OncePerRequestFilter。

接下來開始定義我們的Filter類:

public class AccessLogFilter extends OncePerRequestFilter {
    //... 這里有一些必要的屬性

    @Override
    protected void doFilterInternal(final HttpServletRequest request,
                                    final HttpServletResponse response,
                                    final FilterChain filterChain)
            throws ServletException, IOException {
        // 如果是被排除的 uri,不記錄 access_log
        if (matchExclude(request.getRequestURI())) {
            filterChain.doFilter(request, response);
            return;
        }

        final String requestMethod = request.getMethod();
        final boolean shouldWrapMethod = StringUtils.equalsIgnoreCase(requestMethod, HttpMethod.PUT.name())
                || StringUtils.equalsIgnoreCase(requestMethod, HttpMethod.POST.name());

        final boolean isFirstRequest = !isAsyncDispatch(request);

        final boolean shouldWrapRequest = isFirstRequest && !(request instanceof ContentCachingRequestWrapper) && shouldWrapMethod;
        final HttpServletRequest requestToUse = shouldWrapRequest ? new ContentCachingRequestWrapper(request) : request;

        final boolean shouldWrapResponse = !(response instanceof ContentCachingResponseWrapper) && shouldWrapMethod;
        final HttpServletResponse responseToUse = shouldWrapResponse ? new ContentCachingResponseWrapper(response) : response;

        final long startTime = System.currentTimeMillis();
        Throwable t = null;
        try {
            filterChain.doFilter(requestToUse, responseToUse);
        } catch (Exception e) {
            t = e;
            throw e;
        } finally {
            doSaveAccessLog(requestToUse, responseToUse, System.currentTimeMillis() - startTime, t);
        }
    }

    // ... 這里是一些必要的方法

這段代碼就是整個(gè)邏輯的核心所在,其他的內(nèi)容從源碼中找到。

分析

這個(gè)代碼中,整體的邏輯沒有特別復(fù)雜的地方,只需要注意幾個(gè)關(guān)鍵點(diǎn)就可以了。

  1. 默認(rèn)的HttpServletRequestHttpServletResponse中的流被讀取一次之后,再次讀取會(huì)失敗,所以要使用ContentCachingRequestWrapperContentCachingResponseWrapper進(jìn)行包裝,實(shí)現(xiàn)重復(fù)讀取。
  2. 既然我們可以自定義Filter,那我們依賴的組件中也可能會(huì)自定義Filter,更有可能已經(jīng)對(duì)請(qǐng)求和響應(yīng)對(duì)象進(jìn)行過封裝,所以,一定要先進(jìn)行一步判斷。也就是request instanceof ContentCachingRequestWrapperresponse instanceof ContentCachingResponseWrapper

只要注意了這兩點(diǎn),剩下的都是這個(gè)邏輯的細(xì)化實(shí)現(xiàn)。

運(yùn)行

接下來我們就運(yùn)行一遍,看看結(jié)果。先定義幾種不同的請(qǐng)求:普通 get 請(qǐng)求、普通 post 請(qǐng)求、上傳文件、下載文件,這四個(gè)接口幾乎可以覆蓋絕大部分場景。(因?yàn)槎际潜容^簡單的寫法,源碼就不贅述了,可以從文末的源碼中找到)

先啟動(dòng)項(xiàng)目,然后借助 IDEA 的 http 請(qǐng)求工具:

###普通 get 請(qǐng)求
GET http://localhost:8080/index/get?name=howard

###普通 post 請(qǐng)求
POST http://localhost:8080/index/post
Content-Type: application/json

{"name":"howard"}

###上傳文件
POST http://localhost:8080/index/upload
Content-Type: multipart/form-data; boundary=WebAppBoundary

--WebAppBoundary
Content-Disposition: form-data; name="file"; filename="history.txt"
Content-Type: multipart/form-data
</Users/liuxh/history.txt
--WebAppBoundary--

###下載文件
GET http://localhost:8080/index/download

再看看打印的日志:

2021-04-29 19:44:57.495  INFO 83448 --- [nio-8080-exec-1] c.h.d.s.filter.AccessLogFilter           : time=44ms,ip=127.0.0.1,uri=/index/get,headers=[host:localhost:8080,connection:Keep-Alive,user-agent:Apache-HttpClient/4.5.12 (Java/11.0.7),accept-encoding:gzip,deflate],status=200,requestContentType=null,responseContentType=text/plain;charset=UTF-8,params=name=howard,request=,response=
2021-04-29 19:44:57.551  INFO 83448 --- [nio-8080-exec-2] c.h.d.s.filter.AccessLogFilter           : time=36ms,ip=127.0.0.1,uri=/index/post,headers=[content-type:application/json,content-length:17,host:localhost:8080,connection:Keep-Alive,user-agent:Apache-HttpClient/4.5.12 (Java/11.0.7),accept-encoding:gzip,deflate],status=200,requestContentType=application/json,responseContentType=application/json,params=,request={"name":"howard"},response={"name":"howard","timestamp":"1619696697540"}
2021-04-29 19:44:57.585  INFO 83448 --- [nio-8080-exec-3] c.h.d.s.filter.AccessLogFilter           : time=20ms,ip=127.0.0.1,uri=/index/upload,headers=[content-type:multipart/form-data; boundary=WebAppBoundary,content-length:232,host:localhost:8080,connection:Keep-Alive,user-agent:Apache-HttpClient/4.5.12 (Java/11.0.7),accept-encoding:gzip,deflate],status=200,requestContentType=multipart/form-data; boundary=WebAppBoundary,responseContentType=application/json,params=,request=,response={"contentLength":"0","contentType":"multipart/form-data"}
2021-04-29 19:44:57.626  INFO 83448 --- [nio-8080-exec-4] c.h.d.s.filter.AccessLogFilter           : time=27ms,ip=127.0.0.1,uri=/index/download,headers=[host:localhost:8080,connection:Keep-Alive,user-agent:Apache-HttpClient/4.5.12 (Java/11.0.7),accept-encoding:gzip,deflate],status=200,requestContentType=null,responseContentType=application/octet-stream;charset=utf-8,params=,request=,response=

文末總結(jié)

自定義Filter是比較簡單的,只要能夠注意幾個(gè)關(guān)鍵點(diǎn)就可以了。不過后續(xù)還有擴(kuò)展的空間,比如:

  1. 定義排除的請(qǐng)求 uri,可以借助AntPathMatcher實(shí)現(xiàn) ant 風(fēng)格的定義
  2. 將請(qǐng)求日志單獨(dú)存放,可以借助 logback 或者 log4j2 等框架的的日志配置實(shí)現(xiàn),這樣能更加方便的查找日志
  3. 與調(diào)用鏈技術(shù)結(jié)合,在請(qǐng)求日志中增加調(diào)用鏈的 TraceId 等,可以快速定位待查詢的請(qǐng)求日志

源碼

附上源碼:https://github.com/howardliu-cn/effective-spring/tree/main/spring-filter

推薦閱讀

到此這篇關(guān)于SpringBoot之自定義Filter獲取請(qǐng)求參數(shù)與響應(yīng)結(jié)果案例詳解的文章就介紹到這了,更多相關(guān)SpringBoot之自定義Filter獲取請(qǐng)求參數(shù)與響應(yīng)結(jié)果內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mybatis-Plus中update()和updateById()將字段更新為null

    Mybatis-Plus中update()和updateById()將字段更新為null

    本文主要介紹了Mybatis-Plus中update()和updateById()將字段更新為null,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • Spring詳解四種加載配置項(xiàng)的方法

    Spring詳解四種加載配置項(xiàng)的方法

    這篇文章主要給大家介紹了關(guān)于springboot加載配置項(xiàng)的四種方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用springboot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-06-06
  • Java groovy內(nèi)存回收測試步驟解析

    Java groovy內(nèi)存回收測試步驟解析

    這篇文章主要介紹了Java groovy內(nèi)存回收測試步驟解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • RestTemplate響應(yīng)中如何獲取輸入流InputStream

    RestTemplate響應(yīng)中如何獲取輸入流InputStream

    這篇文章主要介紹了RestTemplate響應(yīng)中如何獲取輸入流InputStream問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • SpringBoot加載不出來application.yml文件的解決方法

    SpringBoot加載不出來application.yml文件的解決方法

    這篇文章主要介紹了SpringBoot加載不出來application.yml文件的解決方法,文中通過示例代碼講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作有一定的幫助,需要的朋友跟著小編來一起來學(xué)習(xí)吧
    2023-12-12
  • win10設(shè)置java環(huán)境變量的方法

    win10設(shè)置java環(huán)境變量的方法

    下面小編就為大家?guī)硪黄獁in10設(shè)置java環(huán)境變量的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-01-01
  • 一文弄懂Java中ThreadPoolExecutor

    一文弄懂Java中ThreadPoolExecutor

    ThreadPoolExecutor是Java中的一個(gè)線程池實(shí)現(xiàn),它可以管理和控制多個(gè) Worker Threads,本文就詳細(xì)的介紹一下Java中ThreadPoolExecutor,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • SpringBoot創(chuàng)建maven多模塊項(xiàng)目實(shí)戰(zhàn)代碼

    SpringBoot創(chuàng)建maven多模塊項(xiàng)目實(shí)戰(zhàn)代碼

    本篇文章主要介紹了SpringBoot創(chuàng)建maven多模塊項(xiàng)目實(shí)戰(zhàn)代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-09-09
  • springMVC圖片上傳的處理方式詳解

    springMVC圖片上傳的處理方式詳解

    這篇文章主要為大家詳細(xì)介紹了springMVC圖片上傳的處理方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • Java設(shè)計(jì)模式之備忘錄模式詳解

    Java設(shè)計(jì)模式之備忘錄模式詳解

    這篇文章主要介紹了Java設(shè)計(jì)模式之備忘錄模式詳解,備忘錄模式在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài)并在該對(duì)象之外保存這個(gè)狀態(tài),這樣以后就可以將該對(duì)象恢復(fù)到原先保存的狀態(tài),需要的朋友可以參考下
    2023-12-12

最新評(píng)論