為什麼HttpServletRequest的輸入流隻能讀一次呢?
當我們調用
getInputStream()
方法擷取輸入流時得到的是一個InputStream對象,而實際類型是ServletInputStream,它繼承于InputStream。
InputStream的
read()
方法内部有一個postion,标志目前流被讀取到的位置,每讀取一次,該标志就會移動一次,如果讀到最後,
read()
會傳回-1,表示已經讀取完了。如果想要重新讀取則需要調用
reset()
方法,position就會移動到上次調用mark的位置,mark預設是0,是以就能從頭再讀了。調用
reset()
方法的前提是已經重寫了
reset()
方法,當然能否reset也是有條件的,它取決于
markSupported()
方法是否傳回true。
InputStream預設不實作reset的相關方法,而ServletInputStream也沒有重寫reset的相關方法,這樣就無法重複讀取流,這就是我們從request對象中擷取的輸入流就隻能讀取一次的原因。
解決辦法:重寫HttpServletRequestWrapper方法
通過重寫HttpServletRequestWrapper把request的儲存下來,然後通過過濾器儲存下來的request在填充進去,這樣就可以多次讀取request了。
重寫HttpServletRequestWrapper
package com.aliyun.icc.core.config.httpHelp;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Enumeration;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class BodyReaderHttpServletRequestWrapper extends
HttpServletRequestWrapper {
private final byte[] body;
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
Enumeration e = request.getHeaderNames() ;
while(e.hasMoreElements()){
String name = (String) e.nextElement();
String value = request.getHeader(name);
System.out.println(name+" = "+value);
}
body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));
}
@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 boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
@Override
public String getHeader(String name) {
return super.getHeader(name);
}
@Override
public Enumeration<String> getHeaderNames() {
return super.getHeaderNames();
}
@Override
public Enumeration<String> getHeaders(String name) {
return super.getHeaders(name);
}
}
再編寫過濾器
package com.aliyun.core.config.filter;
import com.aliyun.core.config.httpHelp.BodyReaderHttpServletRequestWrapper;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class HttpServletRequestReplacedFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if(request instanceof HttpServletRequest &&
(null == request.getParameterMap() || request.getParameterMap().isEmpty())) {
requestWrapper = new RequestReaderHttpServletRequestWrapper((HttpServletRequest) request);
}
//擷取請求中的流如何,将取出來的字元串,再次轉換成流,然後把它放入到新request對象中。
// 在chain.doFiler方法中傳遞新的request對象
if(requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}