软件编程
位置:首页>> 软件编程>> java编程>> Java如何解决发送Post请求报Stream closed问题

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

作者:胡安民  发布时间:2021-12-12 04:20:10 

标签:Java,Post,Stream,closed

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

问题场景

前端发送Post请求,前端返回400 Bad Request,后端Controller层接口也没进去,然后我就开始分析,是啥问题,我通过后端控制台发现HttpMessageNotReadableException 提示信息,这个不是读取请求的消息错误发生的异常吗?

然后我通过IDEA 的DEBUG拦截这个异常发生的位置,然后将相关的代码从新走了一遍发现在

AbstractMessageConverterMethodArgumentResolver->readWithMessageConverters

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

EmptyBodyCheckingHttpInputMessage 是内部类

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

控制台打印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 传输数据,缓存起来,保存到字符串中,之后用的时候将字符串转在转换为流,那么这个字符串就能持续的被复用了

缓存数据

package com.schemautils;
/**
* 解决获取post请求的请求体body只能读取一次问题
*/
import com.alibaba.fastjson.JSONObject;
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 {
        //防止未初始化body,我们手动初始化body ,内部会将body内容初始化到InputStream里
       request.getParameterMap();
       //然后在读取InputStream
      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 JSONObject getBody() {
       return JSONObject.parseObject(this.body);
   }
}

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

package com.schemautils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
//获取请求中的流,将取出来的,再次转换成流,然后把它放入到新request对象中
//必须保证在所有过滤器之前执行,否则就会出现问题(按照首字母进行过滤器优先级A>B>C)
@WebFilter(filterName = "ACacheHttpServletRequestFilter", urlPatterns = "/")
public class CacheHttpServletRequestFilter 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);
       }
       //获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中
       // 在chain.doFiler方法中传递新的request对象
       if(null == requestWrapper) {
           filterChain.doFilter(servletRequest, servletResponse);
       } else {
           filterChain.doFilter(requestWrapper, servletResponse);
       }
   }
   @Override
   public void destroy() {
   }
}

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

@SpringBootApplication(scanBasePackages = "com")
@ServletComponentScan //开启扫描Filter
public class ApplicatioBoot {
   public static void main(String[] args) {
       SpringApplication.run(ApplicatioBoot.class,args);
   }
}

来源:https://blog.csdn.net/weixin_45203607/article/details/123708647

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com