错误如图所示,提示流已经关闭,因为如果通过过滤拦截器读取流的话,IO流关闭只能读取一次, 即使不关闭的话,流有个read标志位,后续控制器会从read标志位开始读,读过流之后就读取不到数据了,除非利用void reset()方法,把pos位置位开始,重新读,但是不是任何流都可以使用,所以我们写通用的方法时候,读取完流中数据之后,需要进行包装request,将流重新写入,供后续控制器读取。
网络上解释如下:那是因为流对应的是数据,数据放在内存中,有的是部分放在内存中。read 一次标记一次当前位置(mark position),第二次read就从标记位置继续读(从内存中copy)数据。 所以这就是为什么读了一次第二次是空了。 怎么让它不为空呢?只要inputstream 中的pos 变成0就可以重写读取当前内存中的数据。javaAPI中有一个方法public void reset() 这个方法就是可以重置pos为起始位置,但是不是所有的IO读取流都可以调用该方法!ServletInputStream是不能调用reset方法,这就导致了只能调用一次getInputStream()。
包装request请求代码如下:
public class MyRequest extends HttpServletRequestWrapper {
private String body="";
public MyRequest(HttpServletRequest request) throws IOException {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
body = stringBuilder.toString();
}
public String getBody(){
return this.body;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
过滤器处理如下图:
拦截器处理如下:
Controller层正常获取post参数即可,利用requestBody等等。
PS:在springcloud项目中,拦截器不能正常从request中读取流,暂未发现是我自己的例子问题还是跟cloud项目相关。