使用ServletInputStream在拦截器或过滤器中应用后重写

 更新时间:2021年10月26日 11:08:35   作者:闫-先生  
这篇文章主要介绍了使用ServletInputStream在拦截器或过滤器中应用后重写,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

ServletInputStream在拦截器或过滤器应用后重写

ServletInputStream inputStream = super.getInputStream();
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
try {
    reader = new BufferedReader(new InputStreamReader(servletInputStream, Charset.forName("UTF-8")));
    String line = "";
    while ((line = reader.readLine()) != null) {
          sb.append(line);
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (servletInputStream != null) {
       try {
           servletInputStream.close();
       } catch (IOException e) {
           e.printStackTrace();
       }
    }
    if (reader != null) {
         try {
              reader.close();
         } catch (IOException e) {
              e.printStackTrace();
         }
    }
}
//使用ServletInputStream中数据的代码
byte[] bytes = sb.getBytes("UTF-8");
final ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
return new ServletInputStream() {
       @Override
       public boolean isFinished() {
               return false;
       }
 
        @Override
        public boolean isReady() {
               return false;
        }
 
        @Override
        public void setReadListener(ReadListener readListener) {
 
        }
 
        @Override
        public int read() throws IOException {
               return bais.read();
        }
};

在拦截器种使用了request.getInputStream()或者getReader()

导致在controller中无法获取请求参数

问题描述

在拦截器种使用了request.getInputStream()或者getReader(),然后在controller接口种使用了@requestbody ,导致controller中无法获取入参,报错:HttpMessageNotReadableException: Required request body is missing:

原因分析

ServletRequest中getReader()和getInputStream()只能调用一次。而又由于@RequestBody注解获取输出参数的方式也是根据流的方式获取的。所以我们前面使用流获取后,后面的@RequestBody就获取不到对应的输入流了。

为什么取不到输入流了???因为流对应的是数据,数据放在内存中,有的是部分放在内存中。

read 一次标记一次当前位置(mark position),第二次read就从标记位置继续读(从内存中copy)数据。

所以这就是为什么读了一次第二次是空了。 怎么让它不为空呢?只要inputstream 中的pos 变成0就可以重写读取当前内存中的数据。

javaAPI中有一个方法public void reset() 这个方法就是可以重置pos为起始位置,但是不是所有的IO读取流都可以调用该方法!ServletInputStream是不能调用reset方法,这就导致了只能调用一次getInputStream()。

如何处理

重写HttpServletRequestWrapper把request保存下来,然后通过过滤器把保存下来的request再填充进去,这样就可以多次读取request了。

第一步:定义过滤器

 
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
 
@WebFilter(urlPatterns = "/*", filterName = "channelFilter")
public class ChannelFilter implements Filter {
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}
 
	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
			throws IOException, ServletException {
		ServletRequest requestWrapper = null;
		if (servletRequest instanceof HttpServletRequest) {
			requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
		}
		if (requestWrapper == null) {
			filterChain.doFilter(servletRequest, servletResponse);
		} else {
			System.out.println("进入了过滤器。。。。。");
			filterChain.doFilter(requestWrapper, servletResponse);
		}
	}
 
	@Override
	public void destroy() {
	}
}

第二步:重写RequestWrapper类

 
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
 
public class RequestWrapper extends HttpServletRequestWrapper { 
	private final String body; 
	public RequestWrapper(HttpServletRequest request) {
		super(request);
		StringBuilder stringBuilder = new StringBuilder();
		BufferedReader bufferedReader = null;
		InputStream inputStream = null;
		try {
			inputStream = request.getInputStream();
			if (inputStream != null) {
				bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
				char[] charBuffer = new char[128];
				int bytesRead = -1;
				while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
					stringBuilder.append(charBuffer, 0, bytesRead);
				}
			} else {
				stringBuilder.append("");
			}
		} catch (IOException ex) {
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (bufferedReader != null) {
				try {
					bufferedReader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		body = stringBuilder.toString();
	} 
	@Override
	public ServletInputStream getInputStream() throws IOException {
		final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
		ServletInputStream servletInputStream = new ServletInputStream() {
			@Override
			public boolean isFinished() {
				return false;
			}
 
			@Override
			public boolean isReady() {
				return false;
			}
 
			@Override
			public void setReadListener(ReadListener readListener) {
			}
 
			@Override
			public int read() throws IOException {
				return byteArrayInputStream.read();
			}
		};
		return servletInputStream;
	}
 
	@Override
	public BufferedReader getReader() throws IOException {
		return new BufferedReader(new InputStreamReader(this.getInputStream()));
	} 
	public String getBody() {
		return this.body;
	}
}

第三步:在启动类中注册过滤器

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java中的拦截器、过滤器、监听器用法详解

    Java中的拦截器、过滤器、监听器用法详解

    这篇文章主要介绍了Java中的拦截器、过滤器、监听器用法,详细分析了Java拦截器、过滤器、监听器的功能、使用方法及相关注意事项,需要的朋友可以参考下
    2017-05-05
  • SpringBoot集成阿里巴巴Druid监控的示例代码

    SpringBoot集成阿里巴巴Druid监控的示例代码

    这篇文章主要介绍了SpringBoot集成阿里巴巴Druid监控的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • Java实现并查集

    Java实现并查集

    这篇文章主要为大家详细介绍了Java实现并查集,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07
  • 解析Tomcat 6、7在EL表达式解析时存在的一个Bug

    解析Tomcat 6、7在EL表达式解析时存在的一个Bug

    这篇文章主要是对Tomcat 6、7在EL表达式解析时存在的一个Bug进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
    2013-12-12
  • 详解Java线程池的使用及工作原理

    详解Java线程池的使用及工作原理

    在日常开发过程中总是以单线程的思维去编码,没有考虑到在多线程状态下的运行状况.由此引发的结果就是请求过多,应用无法响应.为了解决请求过多的问题,又衍生出了线程池的概念.本文记录了Java中线程池的使用及工作原理,需要的朋友可以参考下
    2021-05-05
  • 在Mybatis中使用自定义缓存ehcache的方法

    在Mybatis中使用自定义缓存ehcache的方法

    这篇文章主要介绍了在Mybatis中使用自定义缓存ehcache的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-01-01
  • Spring测试 其实很简单

    Spring测试 其实很简单

    这篇文章主要为大家详细介绍了Spring测试,其实很简单,揭开集成测试神秘的面纱,感兴趣的小伙伴们可以参考一下
    2016-07-07
  • 基于mybatis-plus QueryWrapper 排序的坑

    基于mybatis-plus QueryWrapper 排序的坑

    这篇文章主要介绍了mybatis-plus QueryWrapper 排序的坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • Java中使用BigDecimal进行浮点数运算

    Java中使用BigDecimal进行浮点数运算

    这篇文章主要介绍了Java中使用BigDecimal进行浮点数运算,需要的朋友可以参考下
    2014-07-07
  • 教你使用springSecurity+jwt实现互踢功能

    教你使用springSecurity+jwt实现互踢功能

    JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。接下来通过本文给大家介绍springSecurity+jwt实现互踢功能,需要的朋友可以参考下
    2021-11-11

最新评论