ContentCachingRequestWrapper个人理解,如有理解错误的地方,欢迎指正
参考这篇博客
https://blog.youkuaiyun.com/f641385712/article/details/87814153
背景:request.getInputStream()获取
请求body里面的内容只能被获取一次,ContentCachingRequestWrapper通过这个类能够解决解决HttpServletRequest inputStream只能读取一次的问题,但是这个类有缺陷(前提必须是doFilter之前不能使用request.getInputStream()方法
)
分析:
cachedContent来缓存body体里面的内容。但最重要的是,什么时候才会向cachedCotent里面写内容呢?
1.程序在运行过程中会调用ContentCachingRequestWrapper的getInputStream()方法
public ServletInputStream getInputStream() throws IOException {
if (this.inputStream == null) {
this.inputStream = new ContentCachingRequestWrapper.ContentCachingInputStream(this.getRequest().getInputStream());
}
return this.inputStream;
}
注意,这里使用了一次getInputStream()方法
2.随后调用请求流的read()方法,把内容缓存起来(@RequestBody注解的参数解析器(RequestResponseBodyMethodProcessor)就是调用了read()方法去获取到内容的)
private class ContentCachingInputStream extends ServletInputStream {
private final ServletInputStream is;
public ContentCachingInputStream(ServletInputStream is) {
this.is = is;
}
public int read() throws IOException {
int ch = this.is.read();
if (ch != -1) {
ContentCachingRequestWrapper.this.cachedContent.write(ch);
}
return ch;
}
}
改善这种缺陷的方案:第一次读取了inputStream之后,缓存起来,然后再通过重写getInputStream()方法再将数据写回去
下面这个代码是参考上面那个链接的
public class ContentCachingRequestWrapper extends HttpServletRequestWrapper{
private byte[] body;
private BufferedReader reader;
private ServletInputStream inputStream;
public ContentCachingRequestWrapper(HttpServletRequest request) throws IOException{
super(request);
//读一次 然后缓存起来
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;
}
//代理一下ServletInputStream 里面真是内容为当前缓存的bytes
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();
}
@Override
public boolean isFinished() {
return inputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readlistener) {
}
}
}
或者我这种写法
import org.springframework.util.StringUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
public class RequestWrapper extends HttpServletRequestWrapper {
private String requestBody = null;
HttpServletRequest req = null;
private Cookie[] cookies;
public RequestWrapper(HttpServletRequest request, String requestBody) throws IOException {
super(request);
this.requestBody = requestBody;
this.req = request;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new StringReader(requestBody));
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
private InputStream in = new ByteArrayInputStream(
requestBody.getBytes(StringUtils.isEmpty(req.getCharacterEncoding())?"UTF-8":req.getCharacterEncoding()));
@Override
public int read() throws IOException {
return in.read();
}
};
}
@Override
public Cookie[] getCookies() {
return cookies;
}
public void setCookies(Cookie[] cookies) {
this.cookies = cookies;
}
}