springboot 解决InputStream只能读取一次的问题
通常在springboot应用中,对接口的安全性有要求时都会对请求参数做一些签名验证,这些验证逻辑一般都是统一放到过滤器或拦截器里,这样就不用每个接口都去重复编写验签的逻辑。对于接口有可能接收不同类型的数据,对于表单数据来说,只要调用request的getParameterMap就能全部取出来。对于json数据来说,需要通过request的输入流去读取。
但问题在于request的输入流只能读取一次不能重复读取,所以我们在过滤器或拦截器里读取了request的输入流之后,请求走到controller层时就会报错。而本文的目的就是介绍如何解决在这种场景下遇到HttpServletRequest的输入流只能读取一次的问题。
1|0HttpServletRequest的输入流只能读取一次的原因
HttpServletRequest 对象中调用getInputStream()方法获取输入流时得到的是一个InputStream对象,而实际类型是ServletInputStream,它继承于InputStream。InputStream的read()方法内部有一个postion,标志当前流被读取到的位置,每读取一次,该标志就会移动一次,如果读到最后,read()会返回-1,表示已经读取完了。如果想要重新读取则需要调用reset()方法,position就会移动到上次调用mark的位置,mark默认是0,所以就能从头再读了。调用reset()方法的前提是已经重写了reset()方法,当然能否reset也是有条件的,它取决于markSupported()方法是否返回true。但是InputStream默认不实现reset(),并且markSupported()默认也是返回false。
查看源码截图如下:
ServletInputStream 查看源码得知,ServletInputStream也没有重写reset的相关方法,这样就无法重复读取流,这就是我们从request对象中获取的输入流就只能读取一次的原因。
2|0解决InputStream不能重复读取问题
我们的JavaEE提供了一个 HttpServletRequestWrapper类,HttpServletRequestWrapper集成ServletRequestWrapper并且实现HttpServletRequest接口。在查看源码得知,该类并没有真正去实现HttpServletRequest的方法,而只是在方法内又去调用HttpServletRequest的方法,因此我们可以通过继承该类并实现想要重新定义的方法以达到包装原生HttpServletRequest对象的目的。
具体的实现代码如下:
- 定义一个request容器,包装原来框架的request对象,用于储存请求数据,多次读去请求数据
- 过滤器
在过滤器里将原生的HttpServletRequest对象替换成我们的RequestWrapper对象
- 拦截器
在拦截器中去读取json格式数据,如果不用包装request容器,则request传递到controller层使用@RequestBody注解解析json时会报错,因此在拦截器中想要读取请求body,需要使用到request封装容器。
- 配置拦截器
以上完成了,request的二次封装,但是如果还想读取响应的数据,则需要定义ResponseBodyAdvice
- 自定义ResponseBodyAdvice
__EOF__

本文链接:https://www.cnblogs.com/caicz/p/16531368.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
2021-07-29 分布式事务