HttpServletRequest.getInputStream()多次读取问题
转自:https://www.jianshu.com/p/85feeb30c1ed
HttpServletRequest.getInputStream()多次读取问题
背景
使用POST方法发送数据时,我们习惯于把数据包装成json格式。

有些情况下,我们会在Filter中读取body数据进行数据校验,
GET方法获取参数比较简单。对于POST方法,可使用如下方法从request中获取body参数:
private String getBody(HttpServletRequest request) throws IOException {
InputStream in = request.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in, Charset.forName("UTF-8")));
StringBuffer sb = new StringBuffer("");
String temp;
while ((temp = br.readLine()) != null) {
sb.append(temp);
}
if (in != null) {
in.close();
}
if (br != null) {
br.close();
}
return sb.toString();
}
注意,这里有了一次request.getInputStream()调用。
但是在测试时,一直报JSON格式不正确的错误。经调查发现,项目中使用了公司基础组件中的Filter,而该Filter中也解析了body。同时,不出所料,也是通过调用getInputStream()方法获取的。
原来:
- 一个InputStream对象在被读取完成后,将无法被再次读取,始终返回-1;
- InputStream并没有实现reset方法(可以重置首次读取的位置),无法实现重置操作;
因此,当自己写的Filter中调用了一次getInputStream()后,后面再调用getInputStream()读取的数据都为空,所以才报JSON格式不正确的错误。
解决方法
- 缓存数据
- 使用
HttpServletRequestWrapper进行包装
缓存数据
所谓缓存数据,其实就是调用ServletRequest的setAttribute(String s, Object o)来存储数据。
- 获取到body后,直接缓存
String body = getBody(request);
request.setAttribute("body", body);
优点:
方便
缺点:
不能控制第三方Filter
- 其他地方需要使用body时,只需调用
getAttribute方法就能获取数据了:
request.getAttribute("body");
HttpServletRequestWrapper包装
public class RequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = getBodyStringFromReq(request).getBytes(Charset.forName("UTF-8"));
}
public String getBodyString() {
try {
return new String(body, "UTF-8");
} catch (UnsupportedEncodingException ex) {
return new String(body);
}
}
private String getBodyStringFromReq(ServletRequest request) {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = request.getInputStream();
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
}
在Filter中使用时,FilterChain.doFilter()传入Wrapper对象:
public class TestFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest)request);
String body = requestWrapper.getBodyString();
chain.doFilter(requestWrapper, response); //传入Wrapper对象
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
这样,位于后面的Filter就可以拥有唯一一次调用HttpServletRequest.getInputStream()的机会了。
优点:
不影响第三方Filter
缺点:
多写了这么多代码,麻烦了一些
参考
HttpServletRequest.getInputStream()多次读取问题的更多相关文章
- HttpServletRequest.getInputStream() 只能读取一次
问题:在使用HTTP协议实现应用间接口通信时,服务端读取客户端请求过来的数据,会用到request.getInputStream(),第一次读取的时候可以读取到数据,但是接下来的读取操作都读取不到数据 ...
- request.getInputStream() 流只能读取一次问题
问题: 一次开发过程中同事在 sptring interceptor 中获取 request body 中值,以对数据的校验和预处理等操作 .导致之后spring 在读取request body 值做 ...
- 解决HttpServletRequest的输入流只能读取一次的问题
背景 通常对安全性有要求的接口都会对请求参数做一些签名验证,而我们一般会把验签的逻辑统一放到过滤器或拦截器里,这样就不用每个接口都去重复编写验签的逻辑. 在一个项目中会有很多的接口,而不同的接口可能接 ...
- SpringBoot 解决HttpServletRequest只能读取一次
业务逻辑,通过filter读取请求的request,获取token,并将token传递后面流程使用 BodyReaderHttpServletRequestWrapper: public class ...
- springboot使用百度富文本UEditor遇到的问题一览(springboot controller中request.getInputStream无法读取)
先吐槽一下UEditor作为一个前端的js类库,非要把4种后端的代码给出来,而实际生产中用的框架不同,其代码并不具有适应性.(通常类似其它项目仅仅是给出数据交互的规范.格式,后端实现就可以自由定制) ...
- 多次读取HttpServletRequest的inputstream方法 问题解决
原因:我要收集所有来自前台请求的参数信息,无论在任何地方的.当前请求参数都是json格式,都写在httpservlet的body中.这个只能通过流进行获取.然后问题来了,HttpServletRequ ...
- 解决 request.getInputStream() 只能获取一次body的问题
问题: 在使用HTTP协议实现应用间接口通信时,服务端读取客户端请求过来的数据,会用到request.getInputStream(),第一次读取的时候可以读取到数据,但是接下来的读取操作都读取不到数 ...
- 解决在Filter中读取Request中的流后, 然后再Control中读取不到的做法
摘要: 大家知道, StringMVC中@RequestBody是读取的流的方式, 如果在之前有读取过流后, 发现就没有了. 我们来看一下核心代码: filter中主要做的事情, 就是来校验请求是否合 ...
- springbootboot-HttpServletRequest.getInputStream() 获取post内容
问题描述: 在php端用curl post一段json到java springboot.在java端用request.getInputStream()获取到的数据为空. 问题确认: 询问度娘后, 她告 ...
随机推荐
- MethodImpl 特性
5,MethodImpl 特性 此特性在 System.Runtime.CompilerServices 命名空间中,指定如何实现方法的详细信息. 内联函数使用方法可参考 https://www.wh ...
- 网络类型PAN、LAN、WLAN、CAN、MAN、WAN简单介绍
转至:https://www.cnblogs.com/itmacy/p/12290036.html PAN:personal area network 个人区域网络 无线连接:蓝牙,红外,NFC 有线 ...
- linux多进/线程编程(4)——进程间通信之pipe和fifo
前言: Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间.任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块 ...
- MyBatis动态 order by 排序不生效解决方法
使用Mybatis在做一个项目时,发现需要动态的去做一个排序功能,于是乎有了下面XXXMapper.xml代码 <if test="order!=null and !order.isE ...
- 矩池云 | Tony老师解读Kaggle Twitter情感分析案例
今天Tony老师给大家带来的案例是Kaggle上的Twitter的情感分析竞赛.在这个案例中,将使用预训练的模型BERT来完成对整个竞赛的数据分析. 导入需要的库 import numpy as np ...
- vue轻量进度条
**### vue------ mode 好玩东西+1: 轻量级进度条: 1.引入 import NProgress from 'nprogress'; // progress bar import ...
- Django基础二静态文件和ORM
Django基础二静态文件和ORM 目录 Django基础二静态文件和ORM 1. 静态文件 1.1 静态文件基本配置: 1.2 静态文件进阶配置 2. request参数 3. Django配置数据 ...
- SAS 数值存储方式和精度问题
本文链接:https://www.cnblogs.com/snoopy1866/p/16021137.html 1 数值存储方式 SAS使用8个字节存储数值,使用浮点计数法表示数值. 浮点计数法由4个 ...
- OAuth2和JWT - 如何设计安全的API?
JWT和OAuth2比较? 要比较JWT和OAuth2?首先要明白一点就是,这两个根本没有可比性,是两个完全不同的东西. JWT是一种认证协议 JWT提供了一种用于发布接入令牌(Acce ...
- IC设计基础
一 前言 这一周连续两场线下面试,紧接着又是微信视频面试,从连续三天的面试中,收获颇丰! 存在的问题: 一是对项目细节模糊: 二是IC基础知识薄弱: 具体表现是,在面试过程中,如被问到DDR3和千兆以 ...