好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

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

缓存数据

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

查看更多关于Java如何解决发送Post请求报Stream closed问题的详细内容...

  阅读:23次