这篇文章主要介绍了如何构建可重复读取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*/@Log4j2public 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)) gt; 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); }}
接下来,你需要一个相应的过滤器。
代码如下:
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中添加这个过滤器的配置(省略)。
request中inputStream多次读取
当使用HTTP协议实现应用之间的接口通信时,服务器会使用request.getInputStream()读取客户端请求的数据。第一次读取时可以读取数据,但在下一次读取操作时无法读取数据。
原因
InputStream对象被读取后,不能再次读取,始终返回-1;
InputStream没有实现reset方法(可以重置第一个读取位置),所以无法实现重置操作;
解决方法(缓存读取到的数据)
使用请求、会话等。来缓存读取的数据,这个很容易实现,只要使用setAttribute和getAttribute即可;
使用HttpServletRequestWrapper包装HttpServletRequest,在中初始化读取请求的InputStream数据,以byte[]的形式缓存,然后在Filter中将请求转换为包装后的请求;
代码
编写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;@Slf4jpublic class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper{private final byte[] body;public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException{super(request);Enumerationlt;Stringgt; 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"));}@Overridepublic BufferedReader getReader() throws IOException{return new BufferedReader(new InputStreamReader(getInputStream()));}@Overridepublic ServletInputStream getInputStream() throws IOException{final ByteArrayInputStream bais = new ByteArrayInputStream(body);return new ServletInputStream(){@Overridepublic boolean isFinished(){return false;}@Overridepublic boolean isReady(){return false;}@Overridepublic void setReadListener(ReadListener listener){}@Overridepublic int read() throws IOException{return bais.read();}};}@Overridepublic String getHeader(String name){return super.getHeader(name);}@Overridepublic Enumerationlt;Stringgt; getHeaderNames(){return super.getHeaderNames();}@Overridepublic Enumerationlt;Stringgt; 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);}
以上个人经验,希望能给你一个参考
精彩评论