转自:https://www.jianshu.com/p/85feeb30c1ed

HttpServletRequest.getInputStream()多次读取问题

 

背景

使用POST方法发送数据时,我们习惯于把数据包装成json格式。

 
image.png

有些情况下,我们会在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格式不正确的错误。

解决方法

  1. 缓存数据
  2. 使用HttpServletRequestWrapper进行包装

缓存数据
所谓缓存数据,其实就是调用ServletRequestsetAttribute(String s, Object o)来存储数据。

  1. 获取到body后,直接缓存
String body = getBody(request);
request.setAttribute("body", body);

优点:
方便
缺点:
不能控制第三方Filter

  1. 其他地方需要使用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()多次读取问题的更多相关文章

  1. HttpServletRequest.getInputStream() 只能读取一次

    问题:在使用HTTP协议实现应用间接口通信时,服务端读取客户端请求过来的数据,会用到request.getInputStream(),第一次读取的时候可以读取到数据,但是接下来的读取操作都读取不到数据 ...

  2. request.getInputStream() 流只能读取一次问题

    问题: 一次开发过程中同事在 sptring interceptor 中获取 request body 中值,以对数据的校验和预处理等操作 .导致之后spring 在读取request body 值做 ...

  3. 解决HttpServletRequest的输入流只能读取一次的问题

    背景 通常对安全性有要求的接口都会对请求参数做一些签名验证,而我们一般会把验签的逻辑统一放到过滤器或拦截器里,这样就不用每个接口都去重复编写验签的逻辑. 在一个项目中会有很多的接口,而不同的接口可能接 ...

  4. SpringBoot 解决HttpServletRequest只能读取一次

    业务逻辑,通过filter读取请求的request,获取token,并将token传递后面流程使用 BodyReaderHttpServletRequestWrapper: public class ...

  5. springboot使用百度富文本UEditor遇到的问题一览(springboot controller中request.getInputStream无法读取)

    先吐槽一下UEditor作为一个前端的js类库,非要把4种后端的代码给出来,而实际生产中用的框架不同,其代码并不具有适应性.(通常类似其它项目仅仅是给出数据交互的规范.格式,后端实现就可以自由定制) ...

  6. 多次读取HttpServletRequest的inputstream方法 问题解决

    原因:我要收集所有来自前台请求的参数信息,无论在任何地方的.当前请求参数都是json格式,都写在httpservlet的body中.这个只能通过流进行获取.然后问题来了,HttpServletRequ ...

  7. 解决 request.getInputStream() 只能获取一次body的问题

    问题: 在使用HTTP协议实现应用间接口通信时,服务端读取客户端请求过来的数据,会用到request.getInputStream(),第一次读取的时候可以读取到数据,但是接下来的读取操作都读取不到数 ...

  8. 解决在Filter中读取Request中的流后, 然后再Control中读取不到的做法

    摘要: 大家知道, StringMVC中@RequestBody是读取的流的方式, 如果在之前有读取过流后, 发现就没有了. 我们来看一下核心代码: filter中主要做的事情, 就是来校验请求是否合 ...

  9. springbootboot-HttpServletRequest.getInputStream() 获取post内容

    问题描述: 在php端用curl post一段json到java springboot.在java端用request.getInputStream()获取到的数据为空. 问题确认: 询问度娘后, 她告 ...

随机推荐

  1. 只有PC端的报表工具使用太不方便了!有没有好用的手机报表app?

    马上到年底了,签订的销售合同需尽快回款,销售经理此时正在下午出差的出租车上,通过手中的手机他查看了一下今天的回款报表 项目已经进行到了关键期,项目经理正在奔赴项目城市的高铁列车上,项目今天的进度报表已 ...

  2. 缓冲区(buffer)与缓存(cache) 缓冲:缓解冲击,缓存:临时存储

    缓存与缓冲区 简要概述 缓存(cache):故名思意就是临时存储一下数据的存储器,其他设备可能等下还用的到数据.缓存区可以用来做缓冲区 缓冲区(Buffer):故名意思就是解决设备之间速度不匹配的问题 ...

  3. C# InterFace 接口

    接口设计方式 自顶向下 (如图所示),自底向上. 接口成员: 事件 public interface IDrawingObject { event EventHandler ShapeChanged; ...

  4. 在linux上oracle服务启动停止详细

    转至:https://www.cnblogs.com/baihuitestsoftware/articles/6365431.html 在CentOS 6.3下安装完Oracle 10g R2,重开机 ...

  5. linux 平台实现 web 服务器的自动化发布 (纯shell 版本,存在ssh 不能自动退出问题,待解决)

    转至:https://www.cnblogs.com/vmsky/p/13824172.html 背景说明 1.集团OA系统上线,web App 部署在6台服务器中,因项目初期,每次更新都需要进行大量 ...

  6. jprofiler 查看程序内存泄露

    在最近的工作中,通过JProfiler解决了一个内存泄漏的问题,现将检测的步骤和一些分析记录下来,已备今后遇到相似问题时可以作为参考. 运行环境: Tomcat6,jdk6,JProfiler8 内存 ...

  7. 函数式编程 高阶函数 map&reduce filter sorted

    函数式编程 纯函数:没有变量的函数 对于纯函数而言:只要输入确定,那么输出就是确定的.纯函数是没有副作用的. 函数式编程:允许把函数本身作为参数传入另一个函数,还允许返回一个函数 高阶函数:一个函数的 ...

  8. 根据文件url,下载文件到本地

    /// <summary> /// 根据文件url,下载文件到本地 /// </summary> /// <param name="fileUrl"& ...

  9. 基于SqlSugar的数据库访问处理的封装,支持多数据库并使之适应于实际业务开发中

    在我的各种开发框架中,数据访问有的基于微软企业库,有的基于EFCore的实体框架,两者各有其应用场景,不过多的去比较.最近在使用SqlSugar的时候,觉得这个数据访问处理的组件确实很灵活,据说性能也 ...

  10. tp5 数据库迁移及数据填充

    1:首先通过 composer 安装    原命令加空格 1.* 2:创建 3:填入数据 4:运行,刷新数据库 数据填充: 1:在命令行输入以下命令 composer require fzaninot ...