自定义httpServletRequestWrapper导致上传文件请求参数丢失
问题背景
项目是 SpringBoot 单体式,在项目中,为了实现调用 controller 请求的日志记录功能。因此做了以下配置:
- 创建自定义拦截器 LogInterceptor;
- 因为需要使用到流获取请求参数,解决流只能读一次问题,所以需要自定义 HttpServletRequestWrapper;
- 需要使得自定义 HttpServletRequestWrapper 生效,因此还需要自定义 Filter (HttpServletRequestFilter);
目前在项目中可以正常使用。现在需要把项目的这些配置复制到另一个项目,其中Springboot版本一致,其他三方依赖什么都一样。但是新项目导入文件时,报参数缺失。
排查思路
查询新老项目配置的 filter是否一样?
发现是一样的。并且没有配置多余的filter。
查询新老项目导入文件接口的 controller 请求入参方式是否一样?
发现导入文件时,都是form 表单,content-type 都是 multipart/form-data。(导入文件仅支持form表单,因此第一次排查属于多余!)
怀疑拦截器 LogInterceptor 使用了 request,导致 request数据丢失。

通过debug发现,其实走到 logInterceptor 时,request 数据已经丢失。
既然还没走到拦截器,那可能是自定义 HttpServletRequestWrapper 或者 自定义 HttpServletRequestFilter 问题,接着 debug,发现配置都没问题,可以正常执行。
依次排查了 filter 的加载顺序,发现也没啥用。
排除了 自定义 requestWrapper 和 filter,又发现可以正常获取数据了。然后试着将 requestWrapper 中构造方式的流转换为字节的操作放在每次获取 getInputStream() 方法中,发现也可以。因此怀疑是底层加载出现问题了。

试着对比新老项目所使用的 filter,我通过debug打断点的方式,可以在ApplicationFilterChain的internalDoFilter()方法上打断点。


发现两边不太一样。查询 hiddenHttpMethodFilter过滤器,发现是在 WebMvcAutoConfiguration 自动装配的,但是必须设置 spring.mvc.hiddenmethod.filter.enabled=ture。然后查询项目中 application.yml 配置,发现新老项目这块缺失不一样。然后需要新项目配置,发现问题解决。
根因分析
思考:
为啥调整 requestWrapper 中流转换为字节操作的位置,从构造方式移到 getInputStream() 方法。就可以。
为啥非要使用 hiddenHttpMethodFilter 过滤器,也就能解决问题
分析:
通过百度和查看 hiddenHttpMethodFilter 源码发现,org.apache.catalina.connector.Request.parseParameters() 方法中,判断事先调用过了getInputStream或者getReader,再调用getParameter就不会进行解析了。也就是在一个请求链中,请求对象被前面对象方法中调用request.getInputStream()或request.getReader()获取过内容后,后面的对象方法里再调用这两个方法也无法获取到客户端请求的内容,但是调用request.getParameter()方法获取过内容后,后面的对象方法里依然可以调用它获取到参数的内容。这也就验证了以上两个问题
- 问题1,移动位置后,在 自定义 filter中, 将 httpServletRequest 对象转换成对象时,不执行 getInputStream() 方法,也就解决了问题。
- 问题2,使用 hiddenHttpMethodFilter 过滤器,里边执行了一行代码 String paramValue = request.getParameter(this.methodParam);,这行代码保证了parseParameters()方法优先执行,也能解决问题。
根因:
Tomcat的ServletRequest中, getParameter()方法与getInputStream()/getReader()不兼容, 只能选择一方.调用了一方, 另一方就会是空的(前提:表单的POST请求)。
解决方案
原理已经清楚了,按时会产生出各种不同的问题,因此解决方案需要根据问题场景来说。常见的有:
调整自定义 RequestWrapper 中流转换为字节操作的位置,使其放在 getInputStream() 方法中,这样就不影响底层框架使用。
在 自定义 filter 中,继承 OncePerRequestFilter, 并重写 shouldNotFilter(HttpServletRequest request) 方法,然后只对 Json 请求内容做流只能读取一次的处理(按需引入 Wrapper)。
扩展知识
application/x- www-form-urlencoded 是 Post 请求默认的请求体内容类型,该请求方式是通过调用 request.getParameter() 方法来获取请求参数值。
当请求体内容(注意:get请求没有请求体)类型是 application/x- www-form-urlencoded 时也可以直接调用request.getInputStream()或request.getReader()方法获取到请求内容再解析出具体都参数,但前提是还没调用request.getParameter()方法。此时当request.getInputStream()或request.getReader()获取到请求内容后,无法再调request.getParameter()获取请求内容。即对该类型的请求,三个方法互斥,只能调其中一个。
application/json 是 Post 请求 body 体传参方式,该请求方式是通过调用 request.getInputStream()或request.getReader() 方法来获取请求内容值。
multipart/form-data 是Post 请求文件上传的传参方式,这种比较特殊,经测试,可以使用 request.getParameter() 获取到除文件外的其他参数,获取文件参数,需要使用 request.getInputStream() 。
参考链接:
自定义httpservletrequestwrapper导致form表单提交数据丢失_添加requestwapper后无法获取提交参数
自定义httpServletRequestWrapper导致上传文件请求参数丢失的更多相关文章
- Django之用户上传文件的参数配置
Django之用户上传文件的参数配置 models.py文件 class Xxoo(models.Model): title = models.CharField(max_length=128) # ...
- js上传文件带参数,并且,返回给前台文件路径,解析上传的xml文件,存储到数据库中
ajaxfileupload.js jQuery.extend({ createUploadIframe: function(id, uri) { //create frame var frameId ...
- 【转】php通过curl跨域向asp.net服务器上传文件及参数
转:http://blog.sina.com.cn/s/blog_13331dce50102vq32.html 这是一个由php通过调用asp.net接口向asp.net服务器post上传文件及参数并 ...
- httpclient请求接口,上传文件附加参数(.net core)
/// <summary> /// 上传文件 - 武汉站点 /// </summary> [HttpPost] public IActionResult UploadWH(Re ...
- postman上传文件对参数的contentType类型设置方式
项目中使用postman模拟上传文件接口时,总是不成功,发现content-type设置不对,设置head的contentType后,还是不行,后来无意中发现文件参数默认的content-type类型 ...
- Java模拟http上传文件请求(HttpURLConnection,HttpClient4.4,RestTemplate)
先上代码: public void uploadToUrl(String fileId, String fileSetId, String formUrl) throws Throwable { St ...
- 自定义type='file'上传文件样式
改变默认的上传文件样式: 用label作为替代 <input id="file_-1" type="file" name="file" ...
- SharePoint REST 上传文件请求403错误
最近,需要在SharePoint上传文件到文档库,但是,上传的过程报错了. 错误代码 { "error": { "code": "-213057525 ...
- element-ui upload自定义formdata上传文件和参数
<el-upload list-type="text" action="" :http-request="HandleHttpRequest ...
- curl_setopt用此函数设置上传文件请求的兼容性调整
在用curl_setopt($curl, CURLOPT_POSTFIELDS, $fileData);这个函数设置时会报错如下 curl_setopt(): The usage of the @fi ...
随机推荐
- PPT 放映电脑Office版本低怎么办
转换成全图形PPT 转成图片,无法放动画 转换成全视频 转成视频,无法控制节奏 PPT VIEWEB 本地PPT播放工具
- CompletableFuture 打桌球的应用
CompletableFuture 使用 @Test public void billiardTest() throws Exception { // 创建点外卖线程: CompletableFutu ...
- 【PS算法理论探讨三】 Photoshop中图层样式之 颜色叠加/渐变叠加/图案叠加 算法原理初探讨。
这三个表面上看上去很简单,我们就先描述简单的部分. 颜色叠加:这个和编辑菜单下的填充 颜色 基本是一个意思,相当于在原有的图层上部添加了一个纯实色的虚拟图层,选项里的不透明度和混合模式和普通的概念是一 ...
- 前端知识点 | 查看已登录网站 Cookie 信息
方法一:针对 Chrome 浏览器 设置 \(\to\) 隐私设置和安全性 \(\to\) Cookie 及其他网站数据 \(\to\) 查看所有 Cookie 和网站数据 or chrome://s ...
- python进阶(8)--测试函数与类
一.测试函数(unittest) 1.源文件方法(name_function.py): def get_formatted_name(first,last): """生成 ...
- Vue第六篇 element-ui 项目管理工具npm webpack 启Vue项目vue-cli
node npm npm管理项目 npm init -y npm install xxxx@0.0.0 npm uninstall xxx npm i 下载package.json所有的依赖 webp ...
- kafka 的基本概念及使用场景
本文为博主原创,未经允许不得转载: 1. Kafka 的使用场景: 1.日志收集:一个公司可以用Kafka收集各种服务的log,通过kafka以统一接口服务的方式开放给各种 consumer,例如ha ...
- 文心一言 VS 讯飞星火 VS chatgpt (183)-- 算法导论13.4 7题
七.用go语言,假设用 RB-INSERT 将一个结点 x 插入一棵红黑树,紧接着又用 RB-DELETE 将它从树中删除.结果的红黑树与初始的红黑树是否一样?证明你的答案. 文心一言: 首先,我们需 ...
- APB
APB Usage APB主要用于寄存器和外设(GPIO\TIMER\UART)的访问 CPU 通过AXI接口发出transaction访问外设,AXI-based interconnect接收到AX ...
- SqlSugar DbContext
title: SqlSugar DbContext date: 2023-02-16 20:01:41 tags: SqlSugar categories: ORM description: 总结整理 ...