有时在工程中需要读取多次ServletRequest输入流,比如验签、@Validated校验前原始的数据等。示例代码如下:
package com.zf.example.filter;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Objects;
/**
* 处理输入流只能读第一次问题
* HttpServletRequestWrapper 实现了 HttpServletRequest 接口
*/
public class BodyCopyHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final String UTF_8 = "UTF-8";
/**
* 输入流
*/
private final byte[] bytes;
public BodyCopyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
// 备份
// 如不关心字符集,也可以直接在方法cloneInputStream()中byteArrayOutputStream.toByteArray()
bytes = getBodyString(request).getBytes(Charset.forName(UTF_8));
}
/**
* 获取请求Body
* @param request
* @return
*/
private String getBodyString(final ServletRequest request) {
StringBuilder sb = new StringBuilder();
try (
InputStream inputStream = cloneInputStream(request.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName(UTF_8)))
) {
String line = "";
while (Objects.nonNull((line = reader.readLine()))) {
sb.append(line);
}
} catch (IOException e) {
throw new RuntimeException("输入流读取出错");
}
return sb.toString();
}
/**
* 输入流复制
* @param inputStream
* @return
*/
private InputStream cloneInputStream(ServletInputStream inputStream) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
try {
while ((len = inputStream.read(buffer)) > -1) {
byteArrayOutputStream.write(buffer, 0, len);
}
byteArrayOutputStream.flush();
} catch (IOException e) {
throw new RuntimeException("复制输入流读取出错");
}
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/**
* 重写父方法,返回新的输入流
* @return
* @throws IOException
*/
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream copyStream = new ByteArrayInputStream(bytes);
/**
* 新的输入流
*/
return new ServletInputStream() {
@Override
public int read() throws IOException {
return copyStream.read();
}
/**
* 未读状态
* @return
*/
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
过滤器过滤请求 @WebFilter(filterName = "inputStreamFilter", urlPatterns = "/demo")
package com.zf.example.filter;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 校验前处理。
* WebFilter
*/
@Slf4j
@WebFilter(filterName = "inputStreamFilter", urlPatterns = "/demo")
public class InputStreamFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
ServletRequest requestWrapper = new BodyCopyHttpServletRequestWrapper(httpServletRequest);
// 获取输入流处理
try {
// 获取备份输入流做其他处理,入打印日志、分析校验前参数等
log.info("获取到的输入流:{}", requestWrapper.getInputStream());
} catch (Exception e) {
log.error("异常:", e);
}
filterChain.doFilter(requestWrapper, servletResponse);
}
}
在启动类加入注解 @ServletComponentScan("com.zf.example.filter"),扫描刚才写的过滤器:
package com.zf.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
/**
* spring boot启动类
*/
@SpringBootApplication
@ServletComponentScan("com.zf.example.filter")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@ServletComponentScan注解后,Servlet、Filter、Listener能通过@WebServlet、@WebFilter、@WebListener注解自动注册,不加这个注解可能会导致某些Spring boot版本Filter不生效。