關於springmvc時request的getReader()和getInputStream()只能調用一次的解決辦法


  最近准備在原有的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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM