ServletRequest与servletResponse获取输入输出流
前言:
最近碰到一个业务场景,需要获取request body的消息主题和响应结果,使用过滤器,getInputStream()获取输入流,结果在controller层获取为空,getOutputStream获取响应结果报错
探究:
参考:httpServletRequest中的流只能读取一次的原因 - 一人浅醉- - 博客园 (cnblogs.com)
总结下:
ServletRequest.getInputStream,和@RequestBody底层采用相同的方式获取流,InputStream read方法内部有一个postion,标志当前流读取到的位置,返回-1标志着已经读完,ServletInputStream没有重写reset方法,无法再次去读流
解决方法
通过将 HttpServletRequest 对象包装一层的方式来实现多次读取。
import org.apache.commons.io.IOUtils;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
private BufferedReader reader;
private ServletInputStream inputStream;
public ContentCachingRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
loadBody(request);
}
private void loadBody(HttpServletRequest request) throws IOException{
body = IOUtils.toByteArray(request.getInputStream());
inputStream = new RequestCachingInputStream(body);
}
public byte[] getBody() {
return body;
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (inputStream != null) {
return inputStream;
}
return super.getInputStream();
}
@Override
public BufferedReader getReader() throws IOException {
if (reader == null) {
reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding()));
}
return reader;
}
private static class RequestCachingInputStream extends ServletInputStream {
private final ByteArrayInputStream inputStream;
public RequestCachingInputStream(byte[] bytes) {
inputStream = new ByteArrayInputStream(bytes);
}
@Override
public int read() throws IOException {
return inputStream.read();
}
}
}
HttpServletResponseWrapper
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
public class ContentCachingResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream buffer ;
private ServletOutputStream out ;
private PrintWriter writer ;
public ContentCachingResponseWrapper(HttpServletResponse response) throws IOException {
super(response);
buffer = new ByteArrayOutputStream();
out = new WapperedOutputStream(buffer);
writer = new PrintWriter(new OutputStreamWriter(buffer, "UTF-8"));
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return out;
}
@Override
public PrintWriter getWriter() throws IOException {
return writer;
}
@Override
public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
if (writer != null) {
writer.flush();
}
}
@Override
public void reset() {
buffer.reset();
}
public String getResponseData(String charset) throws IOException {
flushBuffer();//将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据
byte[] bytes = buffer.toByteArray();
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
return "";
}
}
//内部类,对ServletOutputStream进行包装,指定输出流的输出端
private class WapperedOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos = null;
public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException {
bos = stream;
}
//将指定字节写入输出流bos
@Override
public void write(int b) throws IOException {
bos.write(b);
}
@Override
public void write(byte[] b) throws IOException {
bos.write(b, 0, b.length);
}
}
}
过滤器
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) servletRequest);
ContentCachingResponseWrapper contentCachingResponseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) servletResponse);
String body = IOUtils.toString(requestWrapper.getBody(),servletRequest.getCharacterEncoding());
System.out.println(body);
filterChain.doFilter(requestWrapper,contentCachingResponseWrapper);
String result = contentCachingResponseWrapper.getResponseData(servletResponse.getCharacterEncoding());
servletResponse.getOutputStream().write(result.getBytes());
servletResponse.getOutputStream().flush();
servletResponse.getOutputStream().close();
}