自定义HttpServletRequestWrapper报错
2023-02-28 20:56:05 682
Bug代码
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = StreamUtil.readBytes(request.getReader(), "UTF-8");
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
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 listener) {
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}
使用该类是为了在拦截器中打印请求体内容, 且不影响到后续的Controller参数映射
Bug表现
上传文件报错, 其他请求正常
Bug原因
- 当上传文件时, tomcat以默认编码
UTF-8
对上传内容进行解码, 报错, 手动设置编码为ISO-8859-1
可以解决 - 解析上传文件内容时, 会调用
javax.servlet.http.HttpServletRequestWrapper#getParts
, 进而最终调用到org.apache.catalina.connector.Request#getInputStream
时, 判断usingReader
标识位抛出异常. 因为在BodyReaderHttpServletRequestWrapper
的构造方法中已经调用过request.getReader()
了, 该方法将usingReader
标识位由false
改为了true
. 表示该流已经使用过了, 所以调用getParts
报错
修复方法一
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
private final Collection<Part> parts;
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException, ServletException {
super(request);
String contentType = request.getContentType();
// 根据contentType处理请求内容
if (contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
request.setCharacterEncoding(StandardCharsets.ISO_8859_1.name());
parts = request.getParts();
body = new byte[]{};
} else if (contentType.contains(MediaType.APPLICATION_JSON_VALUE) ||
contentType.contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
ServletInputStream stream = request.getInputStream();
String read = IoUtil.read(stream, StandardCharsets.UTF_8);
body = read.getBytes();
parts = new ArrayList<>();
} else {
body = new byte[]{};
parts = new ArrayList<>();
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
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 listener) {
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public Collection<Part> getParts() throws IOException, ServletException {
return parts;
}
}
修复方法二
添加忽略规则, 跳过指定接口