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

x33g5p2x  于2022-03-24 转载在 其他  
字(3.8k)|赞(0)|评价(0)|浏览(2033)

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

问题场景

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

EmptyBodyCheckingHttpInputMessage 是内部类

控制台打印warn 信息如下:

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

缓存数据

  1. package com.schemautils;
  2. /**
  3. * 解决获取post请求的请求体body只能读取一次问题
  4. */
  5. import com.alibaba.fastjson.JSONObject;
  6. import javax.servlet.ReadListener;
  7. import javax.servlet.ServletInputStream;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletRequestWrapper;
  10. import java.io.*;
  11. public class RequestWrapper extends HttpServletRequestWrapper {
  12. private final String body;
  13. public RequestWrapper(HttpServletRequest request) {
  14. super(request);
  15. StringBuilder stringBuilder = new StringBuilder();
  16. BufferedReader bufferedReader = null;
  17. InputStream inputStream = null;
  18. try {
  19. inputStream = request.getInputStream();
  20. if (inputStream != null) {
  21. bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
  22. char[] charBuffer = new char[128];
  23. int bytesRead = -1;
  24. while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
  25. stringBuilder.append(charBuffer, 0, bytesRead);
  26. }
  27. } else {
  28. stringBuilder.append("");
  29. }
  30. } catch (IOException ex) {
  31. } finally {
  32. if (inputStream != null) {
  33. try {
  34. inputStream.close();
  35. }
  36. catch (IOException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. if (bufferedReader != null) {
  41. try {
  42. bufferedReader.close();
  43. }
  44. catch (IOException e) {
  45. e.printStackTrace();
  46. }
  47. }
  48. }
  49. body = stringBuilder.toString();
  50. }
  51. @Override
  52. public ServletInputStream getInputStream() throws IOException {
  53. final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
  54. ServletInputStream servletInputStream = new ServletInputStream() {
  55. @Override
  56. public boolean isFinished() {
  57. return false;
  58. }
  59. @Override
  60. public boolean isReady() {
  61. return false;
  62. }
  63. @Override
  64. public void setReadListener(ReadListener readListener) {
  65. }
  66. @Override
  67. public int read() throws IOException {
  68. return byteArrayInputStream.read();
  69. }
  70. };
  71. return servletInputStream;
  72. }
  73. @Override
  74. public BufferedReader getReader() throws IOException {
  75. return new BufferedReader(new InputStreamReader(this.getInputStream()));
  76. }
  77. public JSONObject getBody() {
  78. return JSONObject.parseObject(this.body);
  79. }
  80. }

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

  1. package com.schemautils;
  2. import org.springframework.core.annotation.Order;
  3. import org.springframework.stereotype.Component;
  4. import javax.servlet.*;
  5. import javax.servlet.annotation.WebFilter;
  6. import javax.servlet.http.HttpServletRequest;
  7. import java.io.IOException;
  8. //获取请求中的流,将取出来的,再次转换成流,然后把它放入到新request对象中
  9. @Component
  10. @WebFilter(filterName = "HttpServletRequestFilter", urlPatterns = "/")
  11. @Order(1)
  12. public class CacheHttpServletRequestFilter implements Filter {
  13. @Override
  14. public void init(FilterConfig filterConfig) throws ServletException {
  15. }
  16. @Override
  17. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  18. ServletRequest requestWrapper = null;
  19. if(servletRequest instanceof HttpServletRequest) {
  20. requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
  21. }
  22. //获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中
  23. // 在chain.doFiler方法中传递新的request对象
  24. if(null == requestWrapper) {
  25. filterChain.doFilter(servletRequest, servletResponse);
  26. } else {
  27. filterChain.doFilter(requestWrapper, servletResponse);
  28. }
  29. }
  30. @Override
  31. public void destroy() {
  32. }
  33. }

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

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

相关文章