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

