MissingServletRequestParameterException 引发深思

  • 一、概述

  •         最近开发过程中,遇到一个问题,在向Tomcat服务器进行请求时候,发现,偶尔出现异常,但是在前端传递param参数,并且这个参数是required=true,但是出现了MissingServletRequestParameterException报错问题。
  •           由于这个问题并不是经常出现,偶尔出现问题,使用Jmeter 作为模拟器,进行请求模拟,结果在模拟过程中有出现了同样MissingServletRequestParameterException报错信息。

  • 传输参数时候,如果某个参数值并没有写 判断时候应该要 先判断 str==null,在去判断 str.isEmpty()

  • 二、问题分析

  •        直接在这个异常类的函数设置断点,查看其为什么抛出这样的异常:
  •        第一步:查看该异常父类结构图: 从结构中看到其属于ServletRequestBlindingException( 说其是属于Request信息中绑定)

  • 但是在 前端传递参数过程,param是required,说明前端肯定传递参数,说明此参数值在传递过程中是不是被Spring框架给吃了?
  • at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:198)
        at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:109)

  • 直接找到handleMissingValue()方法,发现其中并没有解释,直接断点定位到RequestParamMethodArgumentResolver的父类AbstractNamedValueMethodArgumentResolver,

  •    在其父类中我们发现如下代码:
  •  Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest); //在RequestParamMethodArgumentResolver中,直接通过调用request.getParameterValues(name)来获取普通请求的参数
            if (arg == null) {
                if (namedValueInfo.defaultValue != null) {
                    arg = resolveStringValue(namedValueInfo.defaultValue);
                }
                else if (namedValueInfo.required && !nestedParameter.isOptional()) {
                    handleMissingValue(namedValueInfo.name, nestedParameter, webRequest); //如果没有获取到且没有默认值,就会在这里抛出异常。
                }
                arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
            }
            else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
                arg = resolveStringValue(namedValueInfo.defaultValue);
            }

  • 从父类中分析 :request.getParameterValue()里面并没有得到想要值,并且参数是 必须且没有默认的参数值,才会抛出对应异常,因此产生疑问:参数传递过程中难道被框架给吞了????
  • 在此基础上:对Tomcat服务器的源码进行分析:

  • 在此博客中写道: 用户的一个Request请求:将最原始的Socket进行一系列的包装,最后到达HttpServletRequest和HttpServletResponse


  • 最原始的数据保存在org.apache.coyote.Request,Tomcat服务器 中HTTP1Process 类通过service方法传递给coyote,定位到Request请求中参数param出现问题;直接跳到:RequestmappingHandlerAdapter 这个类中
  • at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
    设置断点:对于正常请求:didQueryParamters=false
  •          但是在异常请求中 didQueryParamters=true;
  • 这个参数判断query String 是否已经被解析过,并且在结束时候,会调用paramter的recycle方法,
  • 同时查看 其中对应的recyle()方法:当数据被解析之后,发现recycle()方法会将paramter参数进行清空,并且didQueryParameters 参数置为true.
  •    public void recycle() {
            parameterCount = 0;
            paramHashValues.clear(); //清空了解析后的parameter map
            didQueryParameters=false; //是否被解析过,置成false
            encoding=null;
            decodedQuery.recycle();
            parseFailedReason = null;
        }
    为什么paramters参数值在首次Request中会被解析:
  •    Tomcat 中,Request的对象会被循环使用,因此一定是在进入Spring 框架之前被解析过(或者是被异步执行过),因此在一次获取时候发现其出现 空值异常;
    所以根本原因是,在Parameter被重置了之后,didQueryParameters又被置成了true,导致新的请求参数没有被正确解析,就报错了(此时的parameterMap已经被重置,为空)。而didQueryParameters只有在一种情况下才会被置为true,也就是handleQueryParameters方法被调用时。而handleQueryParameters会在多个场景中被调用,其中一个就是getParameterValues,获取请求参数的值

  • 最终通过全局搜索:引用HttpServeletRequest 地方,最终发现buryPoint()方法,这个方法属于异步执行方法,在请求结束之后依然调用Request.getParamter()方法,导致下一次请求解析参数不被解析:
@Async
    public void buryPoint(long userId, HttpServletRequest request.....) {
        if (request != null) {
           xxx = request.getParameter("xxx");
        }


  •          

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值