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

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

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

标签:Java,Post,Stream,closed
0
投稿

猜你喜欢

  • 详解Java中switch的新特性

    2023-11-24 23:41:54
  • Java8中CompletableFuture的用法全解

    2023-09-08 15:08:55
  • 常用Maven库,镜像库及maven/gradle配置(小结)

    2023-11-20 23:44:00
  • Java实现用户管理系统

    2023-08-26 17:18:21
  • SpringMVC框架post提交数据库出现乱码解决方案

    2022-03-01 09:50:41
  • java通过Callable和Future来接收线程池的执行结果

    2022-03-31 05:01:43
  • Logback日志基础及自定义配置代码实例

    2022-09-04 01:01:41
  • 执行java请求时导致在脚本执行结束时JVM无法退出

    2023-11-25 05:57:13
  • Intellij Idea 多模块Maven工程中模块之间无法相互引用问题

    2023-11-11 07:24:51
  • 使用GSON库将Java中的map键值对应结构对象转换为JSON

    2022-10-21 14:51:47
  • 解决Jenkins集成SonarQube遇到的报错问题

    2023-11-24 08:54:10
  • Java仿12306图片验证码

    2022-09-29 05:36:08
  • SpringBoot中的main方法注入service

    2021-10-31 15:33:33
  • Spring的@Autowired加到接口上但获取的是实现类的问题

    2023-08-23 21:32:21
  • 基于java中的流程控制语句总结(必看篇)

    2023-11-08 09:56:59
  • 源码解析JDK 1.8 中的 Map.merge()

    2023-11-16 23:49:25
  • Java的split方法使用详解

    2021-10-03 06:09:57
  • Java servlet、filter、listener、interceptor之间的区别和联系

    2023-11-02 15:32:17
  • 5种Android数据存储方式汇总

    2023-08-06 06:49:04
  • 详解java WebSocket的实现以及Spring WebSocket

    2023-11-24 13:16:25
  • asp之家 软件编程 m.aspxhome.com