如何構建可重復讀取inputStream的request
構建可重復讀取inputStream的request
我們知道,request的inputStream只能被讀取一次,多次讀取將報錯,那么如何才能重復讀取呢?答案之一是:增加緩沖,記錄已讀取的內容。
代碼如下所示:
import lombok.extern.log4j.Log4j2; import org.springframework.mock.web.DelegatingServletInputStream; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; /** ?* request wrapper: 可重復讀取request.getInputStream ?*/ @Log4j2 public class RepeatedlyReadRequestWrapper extends HttpServletRequestWrapper { ? ? private static final int BUFFER_START_POSITION = 0; ? ? private static final int CHAR_BUFFER_LENGTH = 1024; ? ? /** ? ? ?* input stream 的buffer ? ? ?*/ ? ? private final String body; ? ? /** ? ? ?* @param request {@link javax.servlet.http.HttpServletRequest} object. ? ? ?*/ ? ? public RepeatedlyReadRequestWrapper(HttpServletRequest request) { ? ? ? ? super(request); ? ? ? ? StringBuilder stringBuilder = new StringBuilder(); ? ? ? ? InputStream inputStream = null; ? ? ? ? try { ? ? ? ? ? ? inputStream = request.getInputStream(); ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? log.error("Error reading the request body…", e); ? ? ? ? } ? ? ? ? if (inputStream != null) { ? ? ? ? ? ? try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { ? ? ? ? ? ? ? ? char[] charBuffer = new char[CHAR_BUFFER_LENGTH]; ? ? ? ? ? ? ? ? int bytesRead; ? ? ? ? ? ? ? ? while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { ? ? ? ? ? ? ? ? ? ? stringBuilder.append(charBuffer, BUFFER_START_POSITION, bytesRead); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? ? ? log.error("Fail to read input stream",e); ? ? ? ? ? ? } ? ? ? ? } else { ? ? ? ? ? ? stringBuilder.append(""); ? ? ? ? } ? ? ? ? body = stringBuilder.toString(); ? ? } ? ? @Override ? ? public ServletInputStream getInputStream() throws IOException { ? ? ? ? final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes()); ? ? ? ? return new DelegatingServletInputStream(byteArrayInputStream); ? ? } }
接下來,需要一個對應的Filter.
代碼如下所示:
import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; public class RepeatlyReadFilter 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 { ? ? ? ? if (request instanceof HttpServletRequest) { ? ? ? ? ? ? request = new RepeatedlyReadRequestWrapper((HttpServletRequest) request); ? ? ? ? } ? ? ? ? chain.doFilter(request, response); ? ? } ? ? @Override ? ? public void destroy() { ? ? ? ? //Do nothing ? ? } }
最后,需要在web.xml中,增加該Filter的配置(略)。
request中inputStream多次讀取
在使用HTTP協議實現應用間接口通信時,服務端讀取客戶端請求過來的數據,會用到request.getInputStream(),第一次讀取的時候可以讀取到數據,但是接下來的讀取操作都讀取不到數據。
原因
一個InputStream對象在被讀取完成后,將無法被再次讀取,始終返回-1;
InputStream并沒有實現reset方法(可以重置首次讀取的位置),無法實現重置操作;
解決方法(緩存讀取到的數據)
使用request、session等來緩存讀取到的數據,這種方式很容易實現,只要setAttribute和getAttribute就行;
使用HttpServletRequestWrapper來包裝HttpServletRequest,在中初始化讀取request的InputStream數據,以byte[]形式緩存在其中,然后在Filter中將request轉換為包裝過的request;
代碼
編寫rHttpServletRequestWrapper子類,用來處理請求數據
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; import lombok.extern.slf4j.Slf4j; @Slf4j public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); Enumeration<String> e = request.getHeaderNames(); while (e.hasMoreElements()) { String name = (String) e.nextElement(); String value = request.getHeader(name); log.debug("HttpServletRequest頭信息:{}-{}", 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 listener) { } @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); } }
調用
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; ServletRequest requestWrapper = null; requestWrapper = new BodyReaderHttpServletRequestWrapper(httpRequest); //數據讀取處理 //... //將requestWrapper專遞給后面的過濾器 filterChain.doFilter(requestWrapper, httpResponse); }
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Maven如何構建可執(zhí)行的jar包(包含依賴jar包)
這篇文章主要介紹了Maven如何構建可執(zhí)行的jar包(包含依賴jar包) ,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11Java 可視化垃圾回收_動力節(jié)點Java學院整理
Ben Evans是一名資深培訓師兼顧問,他在演講可視化垃圾回收中從基礎談起討論了垃圾回收。以下是對其演講的簡短總結。感興趣的朋友一起學習吧2017-05-05