servlet读取请求参数后流失效的问题
2024-08-31 09:28:20
在用reset接口的时候,常常会使用request.getInputStream()方法,但是流只能读取一次,一旦想要加上一个过滤器用来检测用户请求的数据时就会出现异常。
在过滤器中通过流读取出用户post提交过来的数据,这是流已经读取了一次,那么该流就已经作废了,所以在contorller再次读取用户请求的数据时就会抛出异常。
解决方法
方法一:
参见:http://www.cnblogs.com/jiangxinnju/p/5709378.html
简单说一下原理,其实就是通过自定义的HttpServletRequestWrapper 备份一下流的数据,自定义HttpServletRequestWrapper 调用父类request.getInputStream()读取全部数据出来保存在一个byte数组内,当再次获取流数据的时候,自定义的HttpServletRequestWrapper 就会用byte数组重新生成一个新的流。备份的流数据仍然保留在byte数组中。
方法二:
request.getInputStream()方法只能使用一次,流就会作废了,其实我们还可以通过另一种方式获取用户传输的数据,那就是通过request.getReader()来获取到一个BufferedReader。这里要说一下BufferedReader是缓存流,并且BufferedReader的markSupported方法是返回true,说明BufferedReader是可以标记和回退的流。
BufferedReader中有defaultCharBufferSize属性
static { BufferedReader.defaultCharBufferSize = 8192; BufferedReader.defaultExpectedLineLength = 80; }
这里可以看出缓存大小为8192,也就是8KB大小的缓存,所以我们可以用BufferedReader的标记和重置方法来进行重复读取流。
方法二的性能会比方法一的性能较快,因为BufferedReader只存在一个实例,而不是每次调用都生成。要注意的是通过BufferedReader的标记和重置方法只能读取8KB以内的内容。超过8KB进行重复读取时,将会清空8KB前的缓存,导致标记失效,缓存内容将会丢失。比如:读取了9KB的内容,那么其中前面的1KB的内容将会在缓存中被清除掉,而只缓存了后面的8KB内容,前面的1KB内容被永久清除了,但后面的8KB内容还是能通过标记重置来进行重复读取。 (8KB内容相当于4000字)
方法一也是可以做成缓存流的形式,代替每次生成一个新的实例,这里就不一一介绍。
方法一也是有缺点的,就是遇到文件上传时内存消耗会增加,因为上传的文件也是在request流中的,一旦上传较大的文件,服务器将会内存不足导致宕机。
建议最好使用原生自带的缓存BufferedReader,会更加方便。
重写request的inputstream方法。。然后在需要部署应用中加上过滤器,在过滤器中加上这个重写的request的方法
如下:编写filter并在web.xml中配置filter
@WebFilter(filterName="accessFilter", urlPatterns={
"*.do",
// "*.jsp",
// "/*",
// "/layout/*",
// "/apply/*",
// "/audit/*",
// "/authority/*",
// "/commonWare/*",
// "/contract/*",
// "/marketing/*",
// "/product/*",
// "/project/*",
// "/system/*",
// "/user/*"
})
public class MyFilter implements Filter {
// 日志对象
private static Logger logger = Logger.getLogger(MyFilter.class); public void init(FilterConfig filterConfig) throws ServletException {
} public void destroy() {
} public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
System.out.println("=====+" + req.hashCode());
HttpServletResponse response = (HttpServletResponse) res;
if ("POST".equalsIgnoreCase(request.getMethod())) { ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
System.out.println("===filter==+" + requestWrapper.hashCode()); String body = HttpUtil.getBodyString(requestWrapper);
System.out.println("AccessFilter="+body);
chain.doFilter(requestWrapper, response);
return ;
} chain.doFilter(req, res);
}
}
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = HttpUtil.getBodyString(request).getBytes(Charset.forName("UTF-8"));
} @Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
} @Override
public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override
public int read() throws IOException {
return bais.read();
}
};
}
}
public static String getBodyString(ServletRequest request) {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = request.getInputStream();
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
参考:https://www.cnblogs.com/a393060727/p/6141295.html
最新文章
- spring security自定义拒绝访问页面
- Navi.Soft20.WinCE使用手册
- 【mysql】用navicat连接虚拟机mysql出现错误代码(10038)
- Log4Net异常日志记录在asp.net mvc3.0的应用(转载)
- bnuoj 16493 Just Pour the Water(矩阵快速幂)
- checkbox选中问题
- css :target
- 基于jQuery开发的手风琴插件 jquery.accordion.js
- UICollectionView中Cell左对齐 居中 右对齐 等间距------你想要的,这里都有
- .net 后台判断是否要替换
- getfacl
- matlab学习(4) any 和cellfun用法
- [C#.net]ListBox对Item进行重绘,设置背景色和前景色
- UESTC - 1324 卿学姐与公主
- java先导课程学习总结
- c++中利用localtime_s函数格式化输出当地日期与时间
- 【读书笔记】iOS-网络-HTTP-请求内容
- iOS 加密的3种方法
- ZH奶酪:PHP (爬虫)下载图片
- ES6 阮一峰阅读学习
热门文章
- pip install xxx Could not fetch URL https://pypi.org/simple/pip/
- vc在x64体系的一般传参数方式
- Centos7編譯安裝LAMP平臺
- 你必须知道的容器日志 (2) 开源日志管理方案 ELK
- Jrebel 激活的方法
- 真的,Kafka 入门一篇文章就够了
- Base系列编码浅析【base16 base32 base64 base85 base36 base 58 base91 base 92 base62】
- linux-PAM
- 封装一个适用于vue的 jsonp
- 从spring boot发邮件聊到开发的友好性