1、问题描述:
spring-boot环境下部分接口使用@RequestBody接收前端传递的复杂json格式数据,在controller中处理完业务逻辑之后,会通过request从流中读取初始请求数据,保存到用户访问日志记录中;当请求进入@RequestBody注解标注的方法之后,一切正常,被标注的复杂对象也获取到了前端传递的数据,但是在后面记录日志读取request输入流时,发现:stream closed,出现了异常(也许此时你会惊讶,what 什么鬼)。
2、原因
经过查找资料发现,spring mvc在处理@RequestBody时,会从request.getInputStream()中读取数据,而该api存在以下特性:
1. 一个InputStream对象在被读取完成后,将无法被再次读取,始终返回-1;
2. InputStream并没有实现reset方法(可以重置首次读取的位置),无法实现重置操作;
也就是说当我图二再次读取流中数据时,此时因为第一次读取完成之后pos指针在末尾,故第二次无法再读取数据(该api也不支持reset pos指针位置)。
3、解决办法
使用HttpServletRequestWrapper来包装HttpServletRequest,在MAPIHttpServletRequestWrapper中初始化读取request的InputStream数据,以byte[]形式缓存在其中,然后在Filter中将request转换为包装过的request;代码如下:
public class MAPIHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body; // 报文
public MAPIHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = readBytes(request.getInputStream());
}
@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 int read() throws IOException {
return bais.read();
}
};
}
/**
* 获取http输入流
* @param inputStream
* @return
*/
public byte[] readBytes(InputStream inputStream) throws IOException {
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
int length=-1;
byte[] bytes=new byte[1024];
while((length=inputStream.read(bytes))!=-1){
byteArrayOutputStream.write(bytes,0,length);
}
return byteArrayOutputStream.toByteArray();
}
}
public class HttpServletRequestReplacedFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if(request instanceof HttpServletRequest) {
requestWrapper = new MAPIHttpServletRequestWrapper((HttpServletRequest) request);
}
if(requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
此处感谢网友解决方案:https://blog.csdn.net/With_Her/article/details/82382942