Request中的InputStream隻能讀取一次的問題
攔截器中通過流 request.getInputStream()的方式讀取body中傳來的資料會導緻controller接收不到值。
ps:在檢視了一些InputStream可以重複讀的方法,其實大多數都講到了,但是沒有講全,原因和結尾沒有說到,原本想複制粘貼完事,結果發現不行,還得自琢磨一下
問題的本質是InputStream讀取完後指針已經到了内容的結尾,對象還是原來的對象,你再次讀取的時候從最後開始讀取,是以肯定是null
解決辦法
- 讓指針讀完後回到原來的起始點
- 利用對象的思想将流的内容儲存起來
1. 讓指針讀完後回到原來的起始點:利用mark做标記和reset回到标記點
由于mark和reset方法隻能在BufferedInputStream中使用,在此不适用這個方法,不能直接用mark和reset
2. 利用對象的思想将流的内容儲存起來:繼承HttpServletRequestWrapper,替換請求過來的Request對象。
是以我們有:
public class RequestWrapper extends HttpServletRequestWrapper {
private static final Logger logger = LoggerFactory.getLogger(RequestWrapper.class);
private byte[] requestBody = null;//用于将流儲存下來
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
requestBody = StreamUtils.copyToByteArray(request.getInputStream());
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
@Override
public int read(){
return bais.read(); // 讀取 requestBody 中的資料
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) { }
};
}
@Override
public BufferedReader getReader() throws IOException{
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}
資料我們已經拿到,那麼原來request的内容已經是不能繼續讀取了,是以我們要将傳進來的Request替換,這樣到controller拿到的才能繼續去讀取内容,是以我們要明白資料在容器的這個傳輸過程:
過程:
Tomcat容器--->Filter--->Servlet--->Interceptor---->controller
因為我是要在Interceptor中拿到資料來驗證,是以我要在攔截器之前替換成可以多次讀取的request對象,是以我要添加攔截器(繼承Filter,注意要注冊攔截器)
攔截器
public class MyFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(MyFilter .class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
logger.info("doFilter---21Line:到達攔截器!");
if((request instanceof HttpServletRequest)) {
logger.info("doFilter---23Line:建立新的request");
requestWrapper = new RequestWrapper((HttpServletRequest) request);
}
if(requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);//替換
}
}
@Override
public void destroy() {
}
注意的地方:
- 問題是解決Request擷取的InputStream隻能讀取一次的問題,如果是讓InputStream讀取多次,可以直接使用第一段代碼就可以實作。
- 對象要繼承HttpServletRequestWrapper,才能實作替換
- 攔截器一定要起作用(注冊别忘記),否則替換不到
閑暇時寫點東西,希望對你有幫助,謝謝!!!