如何構(gòu)建可重復(fù)讀取inputStream的request
構(gòu)建可重復(fù)讀取inputStream的request
我們知道,request的inputStream只能被讀取一次,多次讀取將報錯,那么如何才能重復(fù)讀取呢?答案之一是:增加緩沖,記錄已讀取的內(nèi)容。
代碼如下所示:
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: 可重復(fù)讀取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); ? ? } }
接下來,需要一個對應(yīng)的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協(xié)議實現(xiàn)應(yīng)用間接口通信時,服務(wù)端讀取客戶端請求過來的數(shù)據(jù),會用到request.getInputStream(),第一次讀取的時候可以讀取到數(shù)據(jù),但是接下來的讀取操作都讀取不到數(shù)據(jù)。
原因
一個InputStream對象在被讀取完成后,將無法被再次讀取,始終返回-1;
InputStream并沒有實現(xiàn)reset方法(可以重置首次讀取的位置),無法實現(xiàn)重置操作;
解決方法(緩存讀取到的數(shù)據(jù))
使用request、session等來緩存讀取到的數(shù)據(jù),這種方式很容易實現(xiàn),只要setAttribute和getAttribute就行;
使用HttpServletRequestWrapper來包裝HttpServletRequest,在中初始化讀取request的InputStream數(shù)據(jù),以byte[]形式緩存在其中,然后在Filter中將request轉(zhuǎn)換為包裝過的request;
代碼
編寫rHttpServletRequestWrapper子類,用來處理請求數(shù)據(jù)
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); } }
調(diào)用
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); //數(shù)據(jù)讀取處理 //... //將requestWrapper專遞給后面的過濾器 filterChain.doFilter(requestWrapper, httpResponse); }
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
在Android的應(yīng)用中實現(xiàn)網(wǎng)絡(luò)圖片異步加載的方法
這篇文章主要介紹了在Android的應(yīng)用中實現(xiàn)網(wǎng)絡(luò)圖片異步加載的方法,一定程度上有助于提高安卓程序的使用體驗,需要的朋友可以參考下2015-07-07Jenkins一鍵打包部署SpringBoot應(yīng)用
本文主要介紹了Jenkins一鍵打包部署SpringBoot應(yīng)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01Maven如何構(gòu)建可執(zhí)行的jar包(包含依賴jar包)
這篇文章主要介紹了Maven如何構(gòu)建可執(zhí)行的jar包(包含依賴jar包) ,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11Java 可視化垃圾回收_動力節(jié)點Java學(xué)院整理
Ben Evans是一名資深培訓(xùn)師兼顧問,他在演講可視化垃圾回收中從基礎(chǔ)談起討論了垃圾回收。以下是對其演講的簡短總結(jié)。感興趣的朋友一起學(xué)習(xí)吧2017-05-05