自定义HttpServletRequestWrapper报错

kyaa111 1年前 ⋅ 460 阅读

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原因

  1. 当上传文件时, tomcat以默认编码UTF-8对上传内容进行解码, 报错, 手动设置编码为ISO-8859-1可以解决
  2. 解析上传文件内容时, 会调用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;
	}
}

修复方法二

添加忽略规则, 跳过指定接口