1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        SpringBoot獲取http請(qǐng)求參數(shù),怎么搞?

        共 5453字,需瀏覽 11分鐘

         ·

        2020-09-12 20:27

        點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

        優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

        ? 作者?|??上帝愛(ài)吃蘋(píng)果

        來(lái)源 |? urlify.cn/ryYbi2

        66套java從入門(mén)到精通實(shí)戰(zhàn)課程分享?

        SpringBoot 攔截器獲取http請(qǐng)求參數(shù)—— 所有騷操作基礎(chǔ)

        1、獲取http請(qǐng)求參數(shù)是一種剛需

        我想有的小伙伴肯定有過(guò)獲取http請(qǐng)求的需要,比如想

        1. 前置獲取參數(shù),統(tǒng)計(jì)請(qǐng)求數(shù)據(jù)

        2. 做服務(wù)的接口簽名校驗(yàn)

        3. 敏感接口監(jiān)控日志

        4. 敏感接口防重復(fù)提交

        等等各式各樣的場(chǎng)景,這時(shí)你就需要獲取 HTTP 請(qǐng)求的參數(shù)或者請(qǐng)求body,一般思路有兩種,一種就是自定義個(gè)AOP去攔截目標(biāo)方法,第二種就是使用攔截器。整體比較來(lái)說(shuō),使用攔截器更靈活些,因?yàn)槊總€(gè)接口的請(qǐng)求參數(shù)定義不同,使用AOP很難細(xì)粒度的獲取到變量參數(shù),本文主線(xiàn)是采用攔截器來(lái)獲取HTTP請(qǐng)求。|

        2、定義攔截器獲取請(qǐng)求

        基于 spring-boot-starter-parent 2.1.9.RELEASE

        看起來(lái)這個(gè)很簡(jiǎn)單,這里就直接上code,定義個(gè)攔截器

        /**
        ?*?@author?axin
        ?*?@summary?HTTP請(qǐng)求攔截器
        ?*/
        @Slf4j
        public?class?RequestInterceptor?implements?HandlerInterceptor?{

        ????@Override
        ????public?boolean?preHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler)?throws?Exception?{

        ????????//獲取請(qǐng)求參數(shù)
        ????????String?queryString?=?request.getQueryString();
        ????????log.info("請(qǐng)求參數(shù):{}",?queryString);

        ????????//獲取請(qǐng)求body
        ????????byte[]?bodyBytes?=?StreamUtils.copyToByteArray(request.getInputStream());
        ????????String?body?=?new?String(bodyBytes,?request.getCharacterEncoding());

        ????????log.info("請(qǐng)求體:{}",?body);
        ????????return?true;
        ????}
        }


        然后把這個(gè)攔截器配置一下中:

        /**
        ?*?WebMVC配置,你可以集中在這里配置攔截器、過(guò)濾器、靜態(tài)資源緩存等
        ?*/
        @Configuration
        public?class?WebMvcConfig?implements?WebMvcConfigurer?{

        ????@Override
        ????public?void?addInterceptors(InterceptorRegistry?registry)?{
        ????????registry.addInterceptor(new?RequestInterceptor()).addPathPatterns("/**");
        ????}
        }


        定義個(gè)接口測(cè)試一下

        /**
        ?*?@author?axin
        ?*?@summary?提交測(cè)試接口
        ?*/
        @Slf4j
        @RestController
        public?class?MyHTTPController?{

        ????@GetMapping("/v1/get")
        ????public?void?get(@RequestParam("one")?String?one,
        ????????????????????@RequestParam("two")?BigDecimal?number)?{
        ????????log.info("參數(shù):{},{}",?one,?number);
        ????}


        ????@PostMapping("/v1/post")
        ????public?void?check(@RequestBody?User?user)?{

        ????????log.info("{}",?JSON.toJSONString(user));
        ????}
        }


        GET請(qǐng)求獲取請(qǐng)求參數(shù)示例:

        POST請(qǐng)求獲取請(qǐng)求Body示例:

        我們發(fā)現(xiàn)攔截器在獲取HTTP請(qǐng)求的body時(shí)出現(xiàn)了 400 (Required request body is missing: public void com.axin.world.controller.MyHTTPController.check(com.axin.world.domain.User));同時(shí)也發(fā)現(xiàn)攔截器竟然走了兩遍,這又是咋回事呢?

        3、為什么攔截器會(huì)重復(fù)調(diào)兩遍呢?

        其實(shí)是因?yàn)?tomcat截取到異常后就轉(zhuǎn)發(fā)到/error頁(yè)面,就在這個(gè)轉(zhuǎn)發(fā)的過(guò)程中導(dǎo)致了springmvc重新開(kāi)始DispatcherServlet的整個(gè)流程,所以攔截器執(zhí)行了兩次,我們可以看下第二次調(diào)用時(shí)的url路徑:

        4、ServletInputStream(CoyoteInputStream) 輸入流無(wú)法重復(fù)調(diào)用

        而之前出現(xiàn)的 Required request body is missing 錯(cuò)誤 其實(shí)是ServletInputStream被讀取后無(wú)法第二次再讀取了,所以我們要把讀取過(guò)的內(nèi)容存下來(lái),然后需要的時(shí)候?qū)ν馓峁┛杀恢貜?fù)讀取的ByteArrayInputStream。

        對(duì)于MVC的過(guò)濾器來(lái)說(shuō),我們就需要重寫(xiě) ServletInputStream 的 getInputStream()方法。

        5、自定義 HttpServletRequestWrapper

        為了 重寫(xiě) ServletInputStream 的 getInputStream()方法,我們需要自定義一個(gè) HttpServletRequestWrapper :

        /**
        *?@author?Axin
        *?@summary?自定義?HttpServletRequestWrapper?來(lái)包裝輸入流
        */
        public?class?AxinHttpServletRequestWrapper?extends?HttpServletRequestWrapper?{

        ????/**
        ?????*?緩存下來(lái)的HTTP?body
        ?????*/
        ????private?byte[]?body;

        ????public?AxinHttpServletRequestWrapper(HttpServletRequest?request)?throws?IOException?{
        ????????super(request);
        ????????body?=?StreamUtils.copyToByteArray(request.getInputStream());
        ????}

        ????/**
        ?????*?重新包裝輸入流
        ?????*?@return
        ?????*?@throws?IOException
        ?????*/
        ????@Override
        ????public?ServletInputStream?getInputStream()?throws?IOException?{
        ????????InputStream?bodyStream?=?new?ByteArrayInputStream(body);
        ????????return?new?ServletInputStream()?{

        ????????????@Override
        ????????????public?int?read()?throws?IOException?{
        ????????????????return?bodyStream.read();
        ????????????}

        ????????????/**
        ?????????????*?下面的方法一般情況下不會(huì)被使用,如果你引入了一些需要使用ServletInputStream的外部組件,可以重點(diǎn)關(guān)注一下。
        ?????????????*?@return
        ?????????????*/
        ????????????@Override
        ????????????public?boolean?isFinished()?{
        ????????????????return?false;
        ????????????}

        ????????????@Override
        ????????????public?boolean?isReady()?{
        ????????????????return?true;
        ????????????}

        ????????????@Override
        ????????????public?void?setReadListener(ReadListener?readListener)?{

        ????????????}
        ????????};
        ????}
        ????
        ????@Override
        ????public?BufferedReader?getReader()?throws?IOException?{
        ????????InputStream?bodyStream?=?new?ByteArrayInputStream(body);
        ????????return?new?BufferedReader(new?InputStreamReader(getInputStream()));
        ????}
        }

        然后定義一個(gè) DispatcherServlet子類(lèi)來(lái)分派 上面自定義的 AxinHttpServletRequestWrapper :

        /**
        *?@author?Axin
        *?@summary?自定義?DispatcherServlet?來(lái)分派?AxinHttpServletRequestWrapper
        */
        public?class?AxinDispatcherServlet?extends?DispatcherServlet?{

        ????/**
        ?????*?包裝成我們自定義的request
        ?????*?@param?request
        ?????*?@param?response
        ?????*?@throws?Exception
        ?????*/
        ????@Override
        ????protected?void?doDispatch(HttpServletRequest?request,?HttpServletResponse?response)?throws?Exception?{
        ????????super.doDispatch(new?AxinHttpServletRequestWrapper(request),?response);
        ????}
        }

        然后配置一下:

        /**
        ?*?WebMVC配置,你可以集中在這里配置攔截器、過(guò)濾器、靜態(tài)資源緩存等
        ?*/
        @Configuration
        public?class?WebMvcConfig?implements?WebMvcConfigurer?{

        ????@Override
        ????public?void?addInterceptors(InterceptorRegistry?registry)?{
        ????????registry.addInterceptor(new?RequestInterceptor()).addPathPatterns("/**");
        ????}

        ????@Bean
        ????@Qualifier(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        ????public?DispatcherServlet?dispatcherServlet()?{
        ????????return?new?AxinDispatcherServlet();
        ????}

        再調(diào)用一下 POST請(qǐng)求:

        請(qǐng)求成功!

        6、總結(jié)一下 展望一下

        如果你想對(duì)HTTP請(qǐng)求做些騷操作,那么前置獲取HTTP請(qǐng)求參數(shù)是前提,為此文本給出了使用MVC攔截器獲取參數(shù)的樣例。

        在獲取HTTP Body 的時(shí)候,出現(xiàn)了 Required request body is missing 的錯(cuò)誤,同時(shí)攔截器還出現(xiàn)執(zhí)行了兩遍的問(wèn)題,這是因?yàn)?ServletInputStream被讀取了兩遍導(dǎo)致的,tomcat截取到異常后就轉(zhuǎn)發(fā)到 /error 頁(yè)面 被攔截器攔截到了,攔截器也就執(zhí)行了兩遍。

        為此我們通過(guò)自定義 HttpServletRequestWrapper 來(lái)包裝一個(gè)可被重讀讀取的輸入流,來(lái)達(dá)到期望的攔截效果。

        在獲取到HTTP的請(qǐng)求參數(shù)后,我們可以前置做很多操作,比如常用的服務(wù)端接口簽名驗(yàn)證,敏感接口防重復(fù)請(qǐng)求等等。

        個(gè)人水平有限,如果文章有邏輯錯(cuò)誤或表述問(wèn)題還請(qǐng)指出,歡迎一起交流。




        粉絲福利:108本java從入門(mén)到大神精選電子書(shū)領(lǐng)取

        ???

        ?長(zhǎng)按上方鋒哥微信二維碼?2 秒
        備注「1234」即可獲取資料以及
        可以進(jìn)入java1234官方微信群



        感謝點(diǎn)贊支持下哈?


        瀏覽 54
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            色久天 | 午夜伦情电午夜伦hd无字高清 | 春宵秘戏图啪啪无遮掩 | 久久精品国产亚洲AV超碰 | 亚洲女同女同志6969 | 免费看男女做爰 | .asian美白裸体女pics | 国模私拍大尺度裸体av | 18禁 男女无遮挡在线看 | 男人吮乳吃奶视频大全 |