Request中的InputStream只能读取一次的问题
程序员文章站
2022-03-10 12:09:30
...
Request中的InputStream只能读取一次的问题
拦截器中通过流 request.getInputStream()的方式读取body中传来的数据会导致controller接收不到值。
ps:在查看了一些InputStream可以重复读的方法,其实大多数都讲到了,但是没有讲全,原因和结尾没有说到,原本想复制粘贴完事,结果发现不行,还得自琢磨一下
问题的本质是InputStream读取完后指针已经到了内容的结尾,对象还是原来的对象,你再次读取的时候从最后开始读取,所以肯定是null
解决办法
- 让指针读完后回到原来的起始点
- 利用对象的思想将流的内容保存起来
1. 让指针读完后回到原来的起始点:利用mark做标记和reset回到标记点
由于mark和reset方法只能在BufferedInputStream中使用,在此不适用这个方法,不能直接用mark和reset
2. 利用对象的思想将流的内容保存起来:继承HttpServletRequestWrapper,替换请求过来的Request对象。
所以我们有:
public class RequestWrapper extends HttpServletRequestWrapper {
private static final Logger logger = LoggerFactory.getLogger(RequestWrapper.class);
private byte[] requestBody = null;//用于将流保存下来
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
requestBody = StreamUtils.copyToByteArray(request.getInputStream());
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
@Override
public int read(){
return bais.read(); // 读取 requestBody 中的数据
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) { }
};
}
@Override
public BufferedReader getReader() throws IOException{
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}
数据我们已经拿到,那么原来request的内容已经是不能继续读取了,所以我们要将传进来的Request替换,这样到controller拿到的才能继续去读取内容,所以我们要明白数据在容器的这个传输过程:
过程:
Tomcat容器--->Filter--->Servlet--->Interceptor---->controller
因为我是要在Interceptor中拿到数据来验证,所以我要在拦截器之前替换成可以多次读取的request对象,所以我要添加拦截器(继承Filter,注意要注册拦截器)
拦截器
public class MyFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(MyFilter .class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
logger.info("doFilter---21Line:到达拦截器!");
if((request instanceof HttpServletRequest)) {
logger.info("doFilter---23Line:创建新的request");
requestWrapper = new RequestWrapper((HttpServletRequest) request);
}
if(requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);//替换
}
}
@Override
public void destroy() {
}
注意的地方:
- 问题是解决Request获取的InputStream只能读取一次的问题,如果是让InputStream读取多次,可以直接使用第一段代码就可以实现。
- 对象要继承HttpServletRequestWrapper,才能实现替换
- 拦截器一定要起作用(注册别忘记),否则替换不到
闲暇时写点东西,希望对你有帮助,谢谢!!!
下一篇: 把一个整数数组,分成个大小相同的子数组