ServletRequest中getReader()和getInputStream()只能調用一次的解決辦法(轉)


原文地址:http://liwx2000.iteye.com/blog/1542431

原文作者:liwx2000

 

為了提高項目安全性,攔截非法訪問,要給項目增加了一個過濾器,攔截所有的請求,校驗是否有不安全因素。

這個過程就遇到了一個問題:ServletRequest的getReader()和getInputStream()兩個方法只能被調用一次,而且不能兩個都調用。那么如果Filter中調用了一次,在Controller里面就不能再調用了。沒辦法,就去網上搜羅了一通,發現了一個超贊的解決方法。為了記錄備案,無恥的轉了過來,原文地址在上,內容在下。

 

1. 查看了下ServletRequest的說明,如下:

/** 
 * Retrieves the body of the request as binary data using 
 * a {@link ServletInputStream}.  Either this method or  
 * {@link #getReader} may be called to read the body, not both. 
 * 
 * @return          a {@link ServletInputStream} object containing 
 *              the body of the request 
 * 
 * @exception IllegalStateException  if the {@link #getReader} method 
 *                   has already been called for this request 
 * 
 * @exception IOException       if an input or output exception occurred 
 * 
 */  
  
public ServletInputStream getInputStream() throws IOException;   
  
/** 
 * Retrieves the body of the request as character data using 
 * a <code>BufferedReader</code>.  The reader translates the character 
 * data according to the character encoding used on the body. 
 * Either this method or {@link #getInputStream} may be called to read the 
 * body, not both. 
 *  
 * 
 * @return                  a <code>BufferedReader</code> 
 *                      containing the body of the request   
 * 
 * @exception UnsupportedEncodingException  if the character set encoding 
 *                      used is not supported and the  
 *                      text cannot be decoded 
 * 
 * @exception IllegalStateException     if {@link #getInputStream} method 
 *                      has been called on this request 
 * 
 * @exception IOException           if an input or output exception occurred 
 * 
 * @see                     #getInputStream 
 * 
 */  
  
public BufferedReader getReader() throws IOException;  

 

兩個方法都注明方法只能被調用一次,由於RequestBody是流的形式讀取,那么流讀了一次就沒有了,所以只能被調用一次。既然是因為流只能讀一次的原因,那么只要將流的內容保存下來,就可以實現反復讀取了。byte數組允許被多次讀取,而不會丟失內容。下面使用byte數組將流的內容保存下來。

 

2.  工具方法:

先將RequestBody保存為一個byte數組,然后通過Servlet自帶的HttpServletRequestWrapper類覆蓋getReader()和getInputStream()方法,使流從保存的byte數組讀取。然后再Filter中將ServletRequest替換為ServletRequestWrapper。

 

代碼如下:

BodyReaderHttpServletRequestWrapper類包裝ServletRequest,將流保存為byte[],然后將getReader()和getInputStream()方法的流的讀取指向byte[]。

import java.io.BufferedReader;  
import java.io.ByteArrayInputStream;  
import java.io.IOException;  
import java.io.InputStreamReader;  
  
import javax.servlet.ServletInputStream;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletRequestWrapper;  
  
import jodd.JoddDefault;  
import jodd.io.StreamUtil;  
  
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {  
  
    private final byte[] body;  
      
    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request)   
throws IOException {  
        super(request);  
 // body = StreamUtil.readBytes(request.getReader(), JoddDefault.encoding); 
     // 因為http協議默認傳輸的編碼就是iso-8859-1,如果使用utf-8轉碼亂碼的話,可以嘗試使用iso-8859-1
     body = StreamUtil.readBytes(request.getReader(), "iso-8859-1"); } @Override
public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return bais.read(); } }; } }

 

3. 在Filter中的使用:

在Filter中將ServletRequest替換為ServletRequestWrapper

public class HttpServletRequestReplacedFilter implements Filter {  
  
    @Override  
    public void init(FilterConfig filterConfig) throws ServletException {  
        //Do nothing  
    }  
  
    @Override  
    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);  
        }  
          
    }  
  
    @Override  
    public void destroy() {  
        //Do nothing  
    }  
  
}  

 


免責聲明!

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



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