博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于springmvc时request的getReader()和getInputStream()只能调用一次的解决办法
阅读量:6364 次
发布时间:2019-06-23

本文共 3311 字,大约阅读时间需要 11 分钟。

  最近准备在原有的SSM项目的基础上添加完善的日志分析,由于是APP的后台系统,之前在规划APP的时候,并没有在APP上做埋点的处理,而如果想要进行埋点处理的话,对于未能新升级的APP用户来说,就是去了意义,因为只要用户不升级,埋点就不能在他的APP中运行。所以,就考虑到了在后台的入口增加日志的监控。

  想法总是简单,但是在实际实现的过程中却还是遇到了问题。由于APP基本都采用公参的加密校验,然后采用POST请求传递JSON数据。对于一般的请求分析,比如每个时间段的访问量,或者每个方法每个某块的统计都简单,只要在拦截器中新增一个将数据扔到消息队列中,然后在消费端在进行日志的分析和处理即可。然后如果要针对每个用户在什么时间段,做了什么处理,问题就来了,因为这个时候就必须拿到post中的json参数。

  有些同学就说,这不是很简单么,直接request.getParameter()不就可以了吗?NO,post的json数据是通过流的方式传递的,并不可以直接读取。OK,那我们用request.getReader()拿到流然后转成字符串不就可以了么?那么问题来了,流是只能流一遍的,一旦读过了就不会再有了,具体的方法中就拿不到了。说到这里,其实访问根本就不会再进到方法体了,因为springmvc的DispatcherServlet就会抛出异常  getReader() has already been called for。。。。。。

  说了这么多,下面是重点啦,其实很简单,就是在过滤器处理request(如果没有过滤器,那就新增一个即可)

  实现方法:先将RequestBody保存为一个byte数组,然后通过Servlet自带的HttpServletRequestWrapper类覆盖getReader()和getInputStream()方法,使流从保存的byte数组读取。然后再Filter中将ServletRequest替换为ServletRequestWrapper。代码如下:

BodyReaderHttpServletRequestWrapper类包装ServletRequest,将流保存为byte[],然后将getReader()和getInputStream()方法的流的读取指向byte[]

import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;public class AuthFilter implements Filter{        public void destroy() {            }    public void doFilter(ServletRequest request, ServletResponse response,            FilterChain chain) throws IOException, ServletException {        ServletRequest requestWrapper = null;          if(request instanceof HttpServletRequest) {              requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);          }        if(null == requestWrapper) {              chain.doFilter(request, response);          } else {              chain.doFilter(requestWrapper, response);          }    }        /**     * 初始化函数时,需要获取排除在外的url     */    public void init(FilterConfig config) throws ServletException {        }}

引用的类

1 import java.io.BufferedReader; 2 import java.io.ByteArrayInputStream; 3 import java.io.IOException; 4 import java.io.InputStreamReader; 5  6 import javax.servlet.ServletInputStream; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletRequestWrapper; 9 10 import jodd.JoddDefault;11 import jodd.io.StreamUtil;12 13 public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {14     15     private final byte[] body;  16     17     public BodyReaderHttpServletRequestWrapper(HttpServletRequest request)throws IOException {  18         super(request);  19         body = StreamUtil.readBytes(request.getReader(), JoddDefault.encoding);  20     }  21   22     @Override  23     public BufferedReader getReader() throws IOException {  24         return new BufferedReader(new InputStreamReader(getInputStream()));  25     }  26   27     @Override  28     public ServletInputStream getInputStream() throws IOException {  29         final ByteArrayInputStream bais = new ByteArrayInputStream(body);  30         return new ServletInputStream() {  31   32             @Override  33             public int read() throws IOException {  34                 return bais.read();  35             }  36         };  37     }  38 }

 

转载于:https://www.cnblogs.com/ocean-sky/p/6899613.html

你可能感兴趣的文章
教你如何使用U盘启动Windows 7或Windows 8
查看>>
Windows Phone WP照片视频与 Mac 同步
查看>>
linux定时任务Crond基础概念原理介绍01
查看>>
老男孩教育shell考试题猜数字游戏实战
查看>>
关于一些优秀的链接地址
查看>>
CSS display 属性详解
查看>>
Linux文件系统标准FHS
查看>>
我的友情链接
查看>>
常见的网络管理技术之snmp和端口镜像、流镜像
查看>>
abstract class与interface的差异
查看>>
POJ-1007 DNA Sorting
查看>>
H3C交换机基本配置命令明细
查看>>
Heartbeat+DRBD+MFS高可用
查看>>
要感谢那些曾经慢待你的人
查看>>
常见的global cache等待事件
查看>>
第 7 章 多主机管理 - 047 - 管理 Machine
查看>>
CentOS5和6的系统启动流程
查看>>
怎么看域客户端是否继承了组策略
查看>>
linux防止DDoS***
查看>>
6.4 Linked List 重做
查看>>