关于springmvc时request的getReader()和getInputStream()只能调用一次的解决办法

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

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

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

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

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

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

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 {
    
    }
}

引用的类app

 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 }
相关文章
相关标签/搜索