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

解決偶現(xiàn)的MissingServletRequestParameterException異常問題

 更新時間:2021年10月25日 11:39:08   作者:Mr_SeaTurtle_  
這篇文章主要介紹了解決偶現(xiàn)的MissingServletRequestParameterException問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

概述

最近遇到一個偶現(xiàn)的問題,在向服務(wù)端請求的時候,偶爾會出現(xiàn)異常,在請求中的query String 傳遞了參數(shù),卻出現(xiàn)了異常MissingServletRequestParameterException

如下所示:

018-02-05 11:29:34.910 ERROR 41469 --- [a626f375-7f79-4fd2-88be-1db10a3811cb-] [nio-8080-exec-7] c.s.s.f.ValidationGlobalExceptionHandler : org.springframework.web.bind.MissingServletRequestParameterException: Required long parameter 'xxx' is not present
    at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:198)
    at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:109)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

在請求中顯然是帶了這個參數(shù)的,開始時還以為自己眼花,于是又試了一下,結(jié)果接口返回又正常了。

但是,既然錯誤日志出現(xiàn)了,就不能輕易地放過,尤其是這種偶現(xiàn)的錯誤,充滿著風(fēng)險,極有可能在某個關(guān)鍵時刻爆發(fā)。因此決定抽出時間排查這個問題。接下來,就是整個排查過程。

排查過程

具體的請求如下所示:

curl -X GET --header "Accept: */*" http://localhost:8080/xxx/api/v3/xxx/getXX?param=123456

其中param在Controller的方法中被聲明為required=true,也就是必傳參數(shù),而在前后端交互里,這個參數(shù)也是一定會傳的。

既然這是一個偶現(xiàn)的問題,就不可能通過手動一次次地調(diào)用請求進(jìn)行問題的重現(xiàn)。在這里我采用了Jmeter作為請求觸發(fā)器,進(jìn)行30次一批的重復(fù)請求,結(jié)果在跑第一次的時候,就出現(xiàn)了一次請求異常。

反復(fù)跑了兩三次之后,都出現(xiàn)了同樣的結(jié)果,既然問題已經(jīng)確認(rèn),并且能夠穩(wěn)定重現(xiàn),接下來就是如何找到問題的根源并解決這個問題了。

因為有具體的異常類,所以我的第一想法就是在這個異常類的構(gòu)造函數(shù)中加斷點,看看為什么會拋出這個異常。通過調(diào)用棧,在RequestParamMethodArgumentResolver(AbstractNamedValueMethodArgumentResolver)中發(fā)現(xiàn)如下代碼片段

        Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest); //在RequestParamMethodArgumentResolver中,直接通過調(diào)用request.getParameterValues(name)來獲取普通請求的參數(shù)
        if (arg == null) {
            if (namedValueInfo.defaultValue != null) {
                arg = resolveStringValue(namedValueInfo.defaultValue);
            }
            else if (namedValueInfo.required && !nestedParameter.isOptional()) {
                handleMissingValue(namedValueInfo.name, nestedParameter, webRequest); //如果沒有獲取到且沒有默認(rèn)值,就會在這里拋出異常。
            }
            arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
        }
        else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
            arg = resolveStringValue(namedValueInfo.defaultValue);
        }

看到這里的時候心里不禁疑惑,難道真的沒傳參數(shù)?或者是參數(shù)在傳遞的過程丟了?或者…被框架吃了?!

但疑惑不是我想要的,答案才是我想要的。既然request.getParameterValues里面沒有得到想要的值,那就應(yīng)該去找正常請求時,這個值是如何被設(shè)置的。

首先想到的是查看Request的類圖結(jié)構(gòu)。以及請求被處理的流程圖

Request類圖

Request處理流程圖

所以,最原始的數(shù)據(jù)被保存在了org.apache.coyote.Request這個類中,深入這個類,我們就能夠更接近答案。于是直接到了Http11Processor的service方法中,看具體的處理過程。首先在799行加斷點,看看是處理結(jié)果是什么,由于問題是處在parameter上,直接查看此時request中parameters的各種值。

正常請求:

正常請求

異常請求:

異常請求

對比兩者不難發(fā)現(xiàn),異常請求中的queryMB與正常請求中的是一樣的,也就是說我們請求中帶的參數(shù)被傳遞到了服務(wù)器。但異常請求didQueryParameters被置成了true,而從代碼中可以知道,這個代碼實際上是用于判斷query string是否已經(jīng)被解析過了,并且,在請求處理結(jié)束的時候,會調(diào)用parameter的recycle方法

    public void recycle() {
        parameterCount = 0;
        paramHashValues.clear(); //清空了解析后的parameter map
        didQueryParameters=false; //是否被解析過,置成false
        encoding=null;
        decodedQuery.recycle();
        parseFailedReason = null;
    }

由于Tomcat中,Request以及Response對象都是會被循環(huán)使用的,因此這個時候也是整個Request被重置的時候。

所以根本原因是,在Parameter被重置了之后,didQueryParameters又被置成了true,導(dǎo)致新的請求參數(shù)沒有被正確解析,就報錯了(此時的parameterMap已經(jīng)被重置,為空)。

而didQueryParameters只有在一種情況下才會被置為true,也就是handleQueryParameters方法被調(diào)用時。

而handleQueryParameters會在多個場景中被調(diào)用,其中一個就是getParameterValues,獲取請求參數(shù)的值。

到這里,就可以推斷,應(yīng)用中可能存在代碼,在請求結(jié)束之后,仍然通過Request對象獲取其中的參數(shù)值。

全局搜索引用了HttpServletRequest的地方。最終發(fā)現(xiàn)埋點類中有如下代碼

@Async
    public void buryPoint(long userId, HttpServletRequest request.....) {
        if (request != null) {
           xxx = request.getParameter("xxx");
        }

由于這段邏輯是異步執(zhí)行的,因此完全有可能在請求結(jié)束之后,仍然調(diào)用request.getParameter方法,導(dǎo)致下一次的請求參數(shù)不被解析。將此段代碼注釋掉,重新使用Jmeter進(jìn)行請求,錯誤不再出現(xiàn)。

結(jié)論

不要將HttpServletRequest傳遞到任何異步方法中!

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • JAVA正則表達(dá)式校驗qq號碼的方法

    JAVA正則表達(dá)式校驗qq號碼的方法

    Java作為一種開發(fā)語言,有許多值得推薦的地方,但是它一直以來沒有自帶對正則表達(dá)式的支持。下面小編給大家?guī)砹薐AVA正則表達(dá)式校驗qq號碼的方法,需要的朋友參考下吧
    2018-04-04
  • Java開發(fā)中最讓人頭疼的十個bug

    Java開發(fā)中最讓人頭疼的十個bug

    這篇文章主要給大家總結(jié)介紹了關(guān)于Java開發(fā)中最讓人頭疼的十個bug,同樣的bug信息,可能背后有千萬種原因,而我,永遠(yuǎn)都不知道到底是哪一個,努力通過代碼積累盡可能多的bug,并將它們進(jìn)行分類,可以幫你debug節(jié)省了時間,需要的朋友可以參考下
    2021-10-10
  • Java AtomicInteger類使用方法實例講解

    Java AtomicInteger類使用方法實例講解

    這篇文章主要介紹了Java AtomicInteger類使用方法實例講解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • 詳解Java Socket通信封裝MIna框架

    詳解Java Socket通信封裝MIna框架

    Mina異步IO使用的Java底層JNI框架,Mina提供服務(wù)端和客戶端,將我們的業(yè)務(wù)解耦開發(fā),真正做到高內(nèi)聚低耦合的思想。
    2021-06-06
  • Java后端用EL表達(dá)式改進(jìn)JSP

    Java后端用EL表達(dá)式改進(jìn)JSP

    EL 全名為Expression Language,EL的語法很簡單,它最大的特點就是使用上很方便,本文帶你用EL表達(dá)式改進(jìn)JSP,感興趣的朋友來看看吧
    2022-02-02
  • 在springboot中使用AOP進(jìn)行全局日志記錄

    在springboot中使用AOP進(jìn)行全局日志記錄

    這篇文章主要介紹就在springboot中使用AOP進(jìn)行全局日志記錄,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 使用spring?data的page和pageable如何實現(xiàn)分頁查詢

    使用spring?data的page和pageable如何實現(xiàn)分頁查詢

    這篇文章主要介紹了使用spring?data的page和pageable如何實現(xiàn)分頁查詢,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Java類的初始化順序知識點總結(jié)

    Java類的初始化順序知識點總結(jié)

    在本篇文章里小編給大家整理的是關(guān)于Java類的初始化順序知識點總結(jié),需要的朋友們可以學(xué)習(xí)下。
    2020-02-02
  • MyBatis主鍵自增的兩種實現(xiàn)方法

    MyBatis主鍵自增的兩種實現(xiàn)方法

    本文主要介紹了MyBatis主鍵自增的兩種實現(xiàn)方法,主要包括注解方式或配置文件方式來實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-01-01
  • Java類的初始化順序的實現(xiàn)

    Java類的初始化順序的實現(xiàn)

    類的初始化過程是在類加載階段完成的,包括加載、驗證、準(zhǔn)備、解析和初始化等步驟,本文主要介紹了Java類的初始化順序的實現(xiàn),感興趣的可以了解一下
    2023-12-12

最新評論