首页 > 其他分享 >自定义httpServletRequestWrapper导致上传文件请求参数丢失

自定义httpServletRequestWrapper导致上传文件请求参数丢失

时间:2024-01-29 09:37:22浏览次数:21  
标签:httpServletRequestWrapper 请求 自定义 request getParameter 获取 上传 getInputStream

问题背景

项目是 SpringBoot 单体式,在项目中,为了实现调用 controller 请求的日志记录功能。因此做了以下配置:

  1. 创建自定义拦截器 LogInterceptor;
  2. 因为需要使用到流获取请求参数,解决流只能读一次问题,所以需要自定义 HttpServletRequestWrapper;
  3. 需要使得自定义 HttpServletRequestWrapper 生效,因此还需要自定义 Filter (HttpServletRequestFilter);

目前在项目中可以正常使用。现在需要把项目的这些配置复制到另一个项目,其中Springboot版本一致,其他三方依赖什么都一样。但是新项目导入文件时,报参数缺失。

排查思路

  1. 查询新老项目配置的 filter是否一样?

    发现是一样的。并且没有配置多余的filter。

  2. 查询新老项目导入文件接口的 controller 请求入参方式是否一样?

    发现导入文件时,都是form 表单,content-type 都是 multipart/form-data。(导入文件仅支持form表单,因此第一次排查属于多余!)

  3. 怀疑拦截器 LogInterceptor 使用了 request,导致 request数据丢失。

​ 通过debug发现,其实走到 logInterceptor 时,request 数据已经丢失。

  1. 既然还没走到拦截器,那可能是自定义 HttpServletRequestWrapper 或者 自定义 HttpServletRequestFilter 问题,接着 debug,发现配置都没问题,可以正常执行。

  2. 依次排查了 filter 的加载顺序,发现也没啥用。

  3. 排除了 自定义 requestWrapper 和 filter,又发现可以正常获取数据了。然后试着将 requestWrapper 中构造方式的流转换为字节的操作放在每次获取 getInputStream() 方法中,发现也可以。因此怀疑是底层加载出现问题了。

  4. 试着对比新老项目所使用的 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请求)。

解决方案

原理已经清楚了,按时会产生出各种不同的问题,因此解决方案需要根据问题场景来说。常见的有:

  1. 调整自定义 RequestWrapper 中流转换为字节操作的位置,使其放在 getInputStream() 方法中,这样就不影响底层框架使用。

  2. 在 自定义 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() 。

参考链接:

记一次getParameter()获取不到参数问题的排查

自定义httpservletrequestwrapper导致form表单提交数据丢失_添加requestwapper后无法获取提交参数

标签:httpServletRequestWrapper,请求,自定义,request,getParameter,获取,上传,getInputStream
From: https://www.cnblogs.com/dxiaodang/p/17993795

相关文章

  • Typora中上传图片:使用PigGo+Gitee
    设置FFS程序下载安装程序出现两个程序。FreeFileSync是主程序,RealTimeSync用来设置自动同步打开FreeFileSync主程序,点击蓝色设置按钮比较设置界面同步设置界面一般设置双向,即只要有一边变化即同步。为了保险起见,设置保留历史版本最后确定设置需要同步的文件夹......
  • 下载视频并且直接上传到服务器上
    下载视频并且直接上传到服务器上从网络上批量采集视频下载到本地,并且直接通过sftp上传到服务器上;#-*-coding:utf-8-*-importosimporttimefromconcurrent.futuresimportThreadPoolExecutorimportparamikofromyt_dlpimportYoutubeDL#AI视频换脸!史上最强......
  • firefox 怎么自定义搜索引擎
     后面使用的时候,发现Firefox不能自定义搜索引擎了。从baidu、知乎上面搜索的结果,大部分都是打非所问的,不知道偏到哪里了。从stack上面找到的答案,记录一下 1、地址栏打开about:config2、搜索browser.urlbar.update2.engineAliasRefresh3、点击右侧的+4、现在,当您转到“......
  • vue3使用自定义指令实现图片懒加载
    //自定义指令app.directive('lazy',{mounted(el,binding){//绑定的元素,绑定的值//IntersectionObserver可以用来自动监听元素是否进入了设备的可视区域之内,而不需要频繁的计算来做这个判断constobserve=newIntersectionObserver(([{isIntersec......
  • 自定义路由事件
    路由事件相比.NET事件的优点为在最合适的位置编写紧凑的、组织良好的用于处理事件的代码提供了灵活性。Xaml无限套娃的树状结构,可以简单的在逻辑树上堆砌出定制按钮,而不必像Winform那样,动不动就要大费周章的自定义控件,比如带文字和图片的按钮,这时候,路由事件支持在父级控件上解......
  • [office] Excel中2010版使用自定义名称简化计算公式的操作技巧
    假设企业申报工资基数为员工的基本工资,用户可将“基本工资”所在单元格区域命名为“申报工资基数”,今天,小编就教大家在Excel中2010版使用自定义名称简化计算公式的操作技巧。Excel中2010版使用自定义名称简化计算公式的操作步骤选择“定义名称”选项,在“员工基本信......
  • 自定义对象比较器,结果失真怎么办?
    如果自定义对象比较器的结果失真,那么首先需要确认比较器的compare方法是否正确实现。在Java中,compare方法应该返回一个负整数、零或正整数,分别表示第一个参数小于、等于或大于第二个参数。例如,如果我们正在比较两个Student对象,我们可能会根据他们的身高或年龄来排序。但是,如果我......
  • 一对一直播软件开发,node大文件上传的断点续传
    一对一直播软件开发,node大文件上传的断点续传实现思路整体思路比较简单,拿到文件,保存文件唯一性标识,切割文件,分段上传,每次上传一段,根据唯一性标识判断文件上传进度,直到文件的全部片段上传完毕。下面的内容都是伪代码读取文件内容:constinput=document.querySelecto......
  • 为了生成latex如何在sympy中自定义向量函数?适用于自定义类的latex生成。
    在sympy.printing.Printer的_print函数中可以看到一个hook,使得对于每一个类都会尝试寻找对应的_print_{class}函数来处理,因此我们只要利用好这个hook就可以为自定义类创建latex生成逻辑,我试图创建了一个_print_BoldUndefinedFunction函数,但发现它捕获不到(其实是因为BoldUndefinedF......
  • nodejs实现文件上传
    前言随着前端的发展,本属于后端需要处理的一些功能模块也逐渐可以让前端实现。本篇大致记录一下文件上传功能。一、上传文件这里使用express+multer框架constpath=require('path')constfs=require('fs')constexpress=require('express');//4.18.2constmulter=r......