【问题】SpringBoot之GET请求参数偶发性丢失问题

2 篇文章 0 订阅
2 篇文章 0 订阅

一、What

        最近又偶遇一诡异棘手之问题!

        一个用于获取公钥数据的接口,不定期偶尔频频出现“参数不存在”的问题!

        一度怀疑这是前端的锅,虽然前端同学再三以人格担保!

        经过非仔细观察,发现每每出现问题时,“再点一下就好了”!

        错误信息简单明确,是大家熟知的参数缺失异常:Required request parameter 'xxx' for method parameter type String is not present

        But why? what's wrong?

        Anyway,这是再普通不过的一个GET接口!

 二、When

        作为专业人士,咱自不能流于表象,当寻根溯源,探寻问题背后之本质!唯有寻得问题背后之根由,方可得正确解决问题之道!

        Well, firstly, let's look look, what the hell a http go?

         抱着“大胆假设,小心求证”之精神,以“抽丝剥茧”之排除大法,when the 参数 lost?!

  • HTTP请求:传参正确,排除
  • Nginx:接收正常,排除
  • Gateway:url & queryString正常
  • Controller:参数没了。。

        So,What's up? 

二、Where

        种种迹象表明:问题当发生在服务内部而非外部网络或请求转发丢失!

        进一步分析,发现在问题请求中request.queryString()正常,而request.getParameter()值却是没有获取到

        众所周知的,SpringBoot默认内置tomcat容器,SpringMVC则通过request.getParameter方法获取并绑定Controller接口参数!

        因此,初步判断在tomcat获取parameter参数时出现了不明原因问题

        Well,parameter参数的获取过程是怎样的?

  • SpringMVC框架通过DispatcherServlet实现
  • Tomcat接收到外部请求,将由connector通过Processor受理http请求
  • SpringMVC通过request.getParameter获取并绑定Controller接口参数
  • request.getParameter方法在请求处理过程中仅在第一次调用时通过解析queryString获取parameters参数值,并设置didQueryParameter=true标识已解析处理
  • Http请求处理完成,processor通过release方法释放连接重置参数属性,request.recycle方法重置request参数属性(注意:这里连接器及request对象并不会被销毁,connector再次受理新的请求时,将复用连接器、processor及request对象而非创建)

 1、SpringBoot从request获取parameter参数

RequestParamMethodArgumentResolver#resolveName

2、tomcat封装解析参数

request通过Parameters获取parameter参数:

org.apache.catalina.connector.Request#getParameterValues

 3、Parameters从queryString解析封装parameter参数

org.apache.tomcat.util.http.Parameters#handleQueryParameters

可以发现,参数在解析处理后会设置didQueryParameters参数为true,

4、请求处理结束,还原

五、Why

         tomcat机制:

  • tomcat可支持多个service实例(这不是重点)
  • 每个service实例维护了一个connector池
  • 当service接收到一个http请求时,则从连接池中获取connector进行响应处理

 

        连接器Connector通过Processor对应http请求进行响应处理!

        Processor封装了request与response对象:在请求处理开始时进行初始化封装(仅封装参数属性,并不创建对象),请求处理完成后则进行释放重置(仅重置参数属性,并不销毁对象)!

 

         本次问题的根本原因则在于线程中引用了request对象,并在线程中调用了request.getParameter()方法使参数属性didQuerParameter错误而导致http请求无法正确获取参数值!

  • 假设第一次受理http请求的连接器为connector1
  • 请求request在子线程thread1中被引用
  • connector1完成http请求并执行release释放连接,这时request.didQueryParameters值为false
  • 如果子线程thread1处理任务的时间较长,调用了getParameter方法,这时request.didQueryParameters值将再次被更新为true
  • 当tomcat再次通过connector1受理新的http请求时,由于request.didQueryParameters=true,这时新请求调用getParameter方法将不会再解析queryString,因而无法正确获取parameter参数值

 

         测试验证问题,问题重现与预期一致!

六、How

        修复线程中引用request相关代码,问题解决!

  • 牢记tomcat复用connector这一机制,在编码过程中应特别注意!
  • 不要在线程中引用request等任意tomcat相关组件或属性!

参考:spring boot偶发性丢失POST请求参数问题的解决 - checkboxMan的个人空间 - OSCHINA - 中文开源技术交流社区

  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
SpringBoot中,可以使用@RequestParam注解来传递GET请求参数。通过@RequestParam注解,我们可以指定传过来的属性名,并将其绑定到方法的参数上例如,当我们使用下代码处理GET请求时``` @RequestMapping(value = "/test", method = RequestMethod.GET) public String getRequest(@RequestParam String dog){ return dog; } ``` 这里的@RequestParam注解表示我们要传递一个名为"dog"的参数,并将其绑定到方法的参数"dog"上。当我们发送GET请求时,可以通过/test?dog=value的方式传递参数,其中"value"是我们要传递的具体值。 另外,我们还可以在@RequestParam注解中使用"name"属性来指定传递参数时的属性名。例如: ``` @RequestMapping(value = "/test", method = RequestMethod.GET) public String getRequest(@RequestParam(name = "cat") String dog){ return dog; } ``` 这里的@RequestParam注解中的"name"属性值是"cat",表示我们要传递一个名为"cat"的参数,并将其绑定到方法的参数"dog"上。当我们发送GET请求时,可以通过/test?cat=value的方式传递参数。 此外,@RequestParam注解还可以用在RESTful请求接口中。例如: ``` @RequestMapping(value = "/test/{person}", method = RequestMethod.GET) public String getRest(@PathVariable(name = "person") String dog){ return dog; } ``` 在这个例子中,@RequestParam注解用在了RESTful请求接口中。我们可以通过路径变量的方式传递参数,比如/test/value。使用@RequestParam注解中的"name"属性来指定传递参数时的属性名,这里的"name"属性值是"person",表示我们要传递一个名为"person"的参数,并将其绑定到方法的参数"dog"上。 总结起来,在SpringBoot中,通过@RequestParam注解可以方便地传递GET请求参数,并将其绑定到方法的参数上。我们可以通过指定属性名或者在RESTful请求接口中使用路径变量来传递参数。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gary强z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值