在过滤器中多次读取请求体内容
问题
HttpServletRequest接口定义了ServletInputStream getInputStream() throws IOException;来获取HttpBody的二进制流。但是由于ServletInputStream通常是不可重复的读取的Stream(inputStream.markSupported() === false),如果在Filter中读取了inputStream将导致过滤器链中的下一个Filter不能读取inputStream,Spring的HttpMessageConverter也不能读取inputStream,进一步会导致Controller层获取的参数对象(被@RequestBody或@RequestPart注解)为null。
1. 解决提出问题的人
不在Filter中获取请求体内容!这下压力来到了......
2. @ControllerAdvice
使用@ControllerAdvice继承RequestBodyAdviceAdapter或实现RequestBodyAdvice。可以在beforeBodyRead中读取HttpInputMessage但是记得要返回一个带着新InputStream的HttpInputMessage。
注意:使用此方法对于不需要转换请求体到Controller层的请求无法捕获到,比如没有请求体的GET请求
3. 自己实现HttpServletRequest缓存下InputStream内容并在每次getInputStream都返回一个新的InputStream
构建一个新的可以重复读取InputStream的HttpServletRequest,并在filterChain.doFilter中使用此HttpServletRequest。
Example:
public static class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
private final byte[] cachedBody;
public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
InputStream requestInputStream = request.getInputStream();
this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
}
@Override
public ServletInputStream getInputStream() {
return new CachedBodyServletInputStream(this.cachedBody);
}
@Override
public BufferedReader getReader() {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
return new BufferedReader(new InputStreamReader(byteArrayInputStream));
}
public byte[] getCachedBody() {
return cachedBody;
}
public static class CachedBodyServletInputStream extends ServletInputStream {
private InputStream cachedBodyInputStream;
public CachedBodyServletInputStream(byte[] cachedBody) {
this.cachedBodyInputStream = new ByteArrayInputStream(cachedBody);
}
@Override
public boolean isFinished() {
boolean flag;
try {
flag = cachedBodyInputStream.available() == 0;
} catch (IOException e) {
flag = true;
log.error("CachedBodyServletInputStream.isFinished()", e);
}
return flag;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return cachedBodyInputStream.read();
}
}
}但是此示例有个问题,因为super(request)中的request.inputStream已经被读取完成,会导致getParts()&getPart(name)等另一些与请求体内容有关的方法不可用,也会使Controller中的@RequestPart获取的对象为NULL。