现象
filter中获取HttpServletRequest 请求Body中的数据后,报错:getReader() has already been called、request body is missing。
根本原因
ServletRequest输入流只可以获取一次:stream对应数据,数据存储在内存或者部分存储在内存中,在对stream操作时,每read一次mark一次当前位置,第二次read留时就从mark的位置继续读数据(从内存中copy),因此stream读取一次后再次读取为空。
如何避免?更改stream中mark的位置即pos改为0。Java中reset()方法可以重置streammark位置为其实位置,注意:并不是所有的stream都可以调用reset()方法,ServletInputStream恰恰不可以调用reset方法,因此ServletRequest中流只可以调取一次。
解决方案
新建类继承HttpServletRequestWrapper,类中新建对象存储body数据。filter中使用时,使用新建对象类:
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
private String bodyStr;
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
String bodyString = getBodyString(request);
body = bodyString.getBytes(Charset.forName("UTF-8"));
bodyStr=bodyString;
}
public String getBodyStr() {
return bodyStr;
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
public String getBodyString(HttpServletRequest request) throws IOException {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = request.getInputStream();
reader = new BufferedReader(
new InputStreamReader(inputStream, Charset.forName("UTF-8")));
char[] bodyCharBuffer = new char[1024];
int len = 0;
while ((len = reader.read(bodyCharBuffer)) != -1) {
sb.append(new String(bodyCharBuffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
}
Filter中使用
public class filter extends OncePerRequestFilter{
...
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse respnose,FilterChain filterChain) throws ServletException,IOException{
BodyReaderHttpServletRequestWrapper wrapper = new BodyReaderHttpServletRequestWrapper(request);
wrapper.getBodyStr();//获取body json字符串
filterChain.doFilter(wrapper,response);
}
...
}
评论区