文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

SpringBoot拦截器读取流后不能再读取的问题

2024-04-02 19:55

关注

在SpringBoot的拦截器中通过流 ( request.getInputStream() ) 的方式读取body中传来的数据会导致controller接收不到值。

这个问题其实就是一个流读取的问题,众所周知在Java中input流只能读取一次,主要原因是通标记的方法来判断流是否读取完毕(读取位 -1就是流读取完毕)

解决这个问题我能想到两种方式

1.通过修改标记的方式 ( inputstream.markSupported() 方法可以判断这个流是否支持 mark 和 reset 方法。他们分别是标记 和 重新定位流。)

2.将流赋值给一个 byte[] 数组,或者其他变量保存起来。下载读取流时就调用这个数组就行。

第一种方法

再回到问题上来我们可以先使用第一种方法判断 requet 中的inputStream 是否支持标记和重新定位。因为这种方式实现起来比较简单。无需考虑太多。


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
         boolean b = request.getInputStream().markSupported();
         System.out.println(b);
    }
// 输出结果为 false 

上述代码会返回一个 false 那么很明显,request 中的 input 流是不支持标记和重新定位的。

第二种方法

我们再考虑第二种方法,我们需要一个变量保存这个流。并且还要保证再过滤器中和controller中都要拿到这个变量。直接定义一个全局变量获取修改传值方式,都是可以的。全局变量这种方式我就不演示了。

下面是改变传值方式的 demo


  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        ServletInputStream inputStream1 = request.getInputStream();
        // 各种对 inputStream1 处理的操作...
        Object obejct = inputStream1;
        request.setAttribute("Params",obejct);
}

这样就可以再controller那边就可以直接获取 Attribute 中的值。

但是!这样有很大的局限性,例如: 我已经写好了大多数的controller方法体。这时再改用这种方式传值。对于开发人员是一种莫大的痛苦 -_- 于是通过不屑的百度查询到另一种方法 一一一 改写HttpServletRequestWrapper方法。为什么是这个方法?因为他实现了 HttpServletRequest 这个接口。也是我们拦截器接收的 request 要求的类型。首先我们先看一张图。** 注意 filter 和 inteceptor 中间。 **

通过上方这个图我们可以知道,在 Filter 和 Inteceptor 中间有一层Servlet。而Servlet就是提交request的地方。所以我们要重写HttpServletRequest方法只能在Servlet之前。也就是filter 中。下面就是直接上代码了

1.重写 HttpServletRequest


package com.xqw.kyg.util;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.springframework.util.StreamUtils;
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper{
    private byte[] requestBody = null;//用于将流保存下来
    public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        requestBody = StreamUtils.copyToByteArray(request.getInputStream());
    }
    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
        return new ServletInputStream() {
            @Override
            public int read(){
                return bais.read();  // 读取 requestBody 中的数据
            }
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) { }
        };
    }
    @Override
    public BufferedReader getReader() throws IOException{
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}

2.编写Filter


package com.xqw.kyg.filter;
import com.xqw.kyg.util.MyHttpServletRequestWrapper;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
public class HttpServletRequestReplacedFilter implements Filter {
    @Override
    public void destroy() {}
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if(request instanceof HttpServletRequest) {
            requestWrapper = new MyHttpServletRequestWrapper((HttpServletRequest) request);
        }
        if(requestWrapper == null) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }
    @Override
    public void init(FilterConfig arg0) throws ServletException {}
}

到此代码编写就完成了。你现在可以在 过滤器 中读取inputstream数据controller中也可以获取到了。其实代码并不是特别困难。主要是出现BUG能及时的想到原因,和提供解决方案。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯