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()获取到的数据为空. 问题确认: 询问度娘后, 她告 ...
随机推荐
- iredmail邮件系统离线搭建手册-从零到无
--时间:2020年10月20日 --作者:飞翔的小胖猪 概述 前言 iRedMail 是一个基于 Linux/BSD 系统的零成本.功能完备.成熟的邮件服务器解决方案.iRedMail 是一个开源. ...
- MIPI CSI-2 像素打包格式解析
背景 MIPI CSI-2支持YUV.RGB和RAW data三种数据格式,这里是个笼统的叫法,具体又根据不同的像素打包方式细分为具体的格式,打包是什么概念?就是把Sensor采样得到的RGB三个通道 ...
- WPF-ListView单元格设置文字换行
第2-6行 1 <ListView Name="HumidifyEventLog" Style="{StaticResource ListViewStyle}&qu ...
- CoLAKE: 如何实现非结构性语言和结构性知识表征的同步训练
原创作者 | 疯狂的Max 论文CoLAKE: Contextualized Language and Knowledge Embedding 解读 01 背景与动机 随着预训练模型在NLP领域各大任 ...
- nginx: [error] open() "/usr/local/nginx/logs/nginx.pid" failed (2: No such file or directory)
问题场景 服务器重启后,重启nginx时报错nginx: [error] open() "/usr/local/nginx/logs/nginx.pid" failed (2: N ...
- jvm初步理解
1.什么是运行时数据区? 1.什么是运行时数据区 javac 指令:编译java文件生成class文件 java指令:运行class文件即将数据放到jvm中 class文件运行,后将不同的 ...
- 解释一下什么是线程池(thread pool)?
在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以提高服务程序效率的 ...
- 分布式 PostgreSQL 集群(Citus)官方安装指南
单节点 Citus Docker (Mac 与 Linux) Docker 镜像仅用于开发/测试目的, 并且尚未准备好用于生产用途. 您可以使用一个命令在 Docker 中启动 Citus: # st ...
- 合并两个以单链表形式表示的关于x的多项式(基于c语言)
只写函数内部的,不懂得可以看前面一篇文章对链表的实现: pLinklist addBothLinklist(Linklist* first,Linklist* second){ Linklist *n ...
- CentOS7部署Bind
镜像下载.域名解析.时间同步请点击 阿里巴巴开源镜像站 1.简介 DNS(Domain Name System),域名系统,因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问 ...