Java-解决发送Post请求报Stream closed问题

2022-06-25 14:46:20

springboot项目还是ssm等java常用框架都会有这样的问题,解决办法通用

问题场景

前端发送Post请求,,前端返回400 Bad Request,后端Controller层接口也没进去,然后我就开始分析,是啥问题,我通过后端控制台发现HttpMessageNotReadableException 提示信息,这个不是读取请求的消息错误发生的异常吗? 然后我通过IDEA 的DEBUG拦截这个异常发生的位置,然后将相关的代码从新走了一遍发现在
AbstractMessageConverterMethodArgumentResolver->readWithMessageConverters
在这里插入图片描述
EmptyBodyCheckingHttpInputMessage 是内部类
在这里插入图片描述
控制台打印warn 信息如下:

org.springframework.http.converter.HttpMessageNotReadableException:  I/O error while reading input message; nested exception is java.io.IOException: Stream closed

问题分析

这是因为有人在过滤器或者拦截器中对Request的请求体中的数据流读取了一遍导致的,Springboot准备读取Body数据映射到接口的实体类参数时候失败,发现流已经没有内容了,因为在接口数据流传输使用的都是InputStream 这个流只能被读取一次

解决办法

将InputStream 传输数据,缓存起来,保存到字符串中,之后用的时候将字符串转在转换为流,那么这个字符串就能持续的被复用了

缓存数据

packagecom.schemautils;/**
 * 解决获取post请求的请求体body只能读取一次问题
 */importcom.alibaba.fastjson.JSONObject;importjavax.servlet.ReadListener;importjavax.servlet.ServletInputStream;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletRequestWrapper;importjava.io.*;publicclassRequestWrapperextendsHttpServletRequestWrapper{privatefinalString body;publicRequestWrapper(HttpServletRequest request){super(request);StringBuilder stringBuilder=newStringBuilder();BufferedReader bufferedReader=null;InputStream inputStream=null;try{//防止未初始化body,我们手动初始化body ,内部会将body内容初始化到InputStream里
	        request.getParameterMap();//然后在读取InputStream
	       inputStream= request.getInputStream();if(inputStream!=null){
                bufferedReader=newBufferedReader(newInputStreamReader(inputStream));char[] charBuffer=newchar[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();}@OverridepublicServletInputStreamgetInputStream()throwsIOException{finalByteArrayInputStream byteArrayInputStream=newByteArrayInputStream(body.getBytes());ServletInputStream servletInputStream=newServletInputStream(){@OverridepublicbooleanisFinished(){returnfalse;}@OverridepublicbooleanisReady(){returnfalse;}@OverridepublicvoidsetReadListener(ReadListener readListener){}@Overridepublicintread()throwsIOException{return byteArrayInputStream.read();}};return servletInputStream;}@OverridepublicBufferedReadergetReader()throwsIOException{returnnewBufferedReader(newInputStreamReader(this.getInputStream()));}publicJSONObjectgetBody(){returnJSONObject.parseObject(this.body);}}

添加过滤器并且配置缓存类

packagecom.schemautils;importorg.springframework.core.annotation.Order;importorg.springframework.stereotype.Component;importjavax.servlet.*;importjavax.servlet.annotation.WebFilter;importjavax.servlet.http.HttpServletRequest;importjava.io.IOException;//获取请求中的流,将取出来的,再次转换成流,然后把它放入到新request对象中//必须保证在所有过滤器之前执行,否则就会出现问题(按照首字母进行过滤器优先级A>B>C)@WebFilter(filterName="ACacheHttpServletRequestFilter", urlPatterns="/")publicclassCacheHttpServletRequestFilterimplementsFilter{@Overridepublicvoidinit(FilterConfig filterConfig)throwsServletException{}@OverridepublicvoiddoFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain filterChain)throwsIOException,ServletException{ServletRequest requestWrapper=null;if(servletRequestinstanceofHttpServletRequest){
            requestWrapper=newRequestWrapper((HttpServletRequest) servletRequest);}//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中// 在chain.doFiler方法中传递新的request对象if(null== requestWrapper){
            filterChain.doFilter(servletRequest, servletResponse);}else{
            filterChain.doFilter(requestWrapper, servletResponse);}}@Overridepublicvoiddestroy(){}}

在启动类上开启扫描Filter注解

@SpringBootApplication(scanBasePackages="com")@ServletComponentScan//开启扫描FilterpublicclassApplicatioBoot{publicstaticvoidmain(String[] args){SpringApplication.run(ApplicatioBoot.class,args);}}
  • 作者:胡安民
  • 原文链接:https://blog.csdn.net/weixin_45203607/article/details/123708647
    更新时间:2022-06-25 14:46:20