我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

使用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;
	}
}

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何在Java项目中使用过滤器、拦截器和监听器

如何在Java项目中使用过滤器、拦截器和监听器?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。一、拦截器 :是在面向切面编程的就是在你的service或者一个方法,前调用一个
2023-05-31

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录