数据流 getInputStream() 只能读取一次的处理方法

       新做的一个功能,一个中间服务项目,需要A公司调用服务去B公司拿数据回去,流转起来太繁杂所以公司想把服务做成一个项目,把请求的甲方数据存起来备份以便不时之需.

      所以就需要在请求的时候利用切面做一个日志的输出顺便保存到库里,而在取请求参数的时候就出问题了,相同的取参数再请求数据的时候没事,我加了切面之后在切面里请求也没事,两个一起的时候,报空指针了.

       后来发现是使用request.getInputStream()的锅,先在切面调用一次之后,到了controller时,对象属性都为空,但是在实际的开发中往往需要多次读取。这样就需要我们将流写入进去,提供后续使用。


1:由于inputStream只能被读取一次,这时需要将流中数据存储起来,以便后续使用,需要继承HttpServletRequestWrapper类。
 

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

    /**
     * Request请求参数获取处理类
     */
    private final byte[] body;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        String sessionStream = getBodyString(request);
        body = sessionStream.getBytes(Charset.forName("UTF-8"));
    }

    /**
     * 获取请求Body
     *
     * @param request
     * @return
     */
    private String getBodyString(final ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = cloneInputStream(request.getInputStream());
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
    public String getBodyString() {
        return new String(body, Charset.forName("UTF-8"));
    }
    /**
     * Description: 复制输入流
     *
     * @param inputStream
     * @return
     */
    public InputStream cloneInputStream(ServletInputStream inputStream) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        try {
            while ((len = inputStream.read(buffer)) > -1) {
                byteArrayOutputStream.write(buffer, 0, len);
            }
            byteArrayOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        return byteArrayInputStream;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);

        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }

2.过滤器(springboot项目,没有新建一个)

@ServletComponentScan
@WebFilter(urlPatterns = "/attend/*",filterName = "channelFilter")
public class ChannelFilter implements Filter {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("-----------------------Execute filter start---------------------");
        // 防止流读取一次后就没有了, 所以需要将流继续写出去
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest);
        filterChain.doFilter(requestWrapper, servletResponse);
    }
}

注意:@WebFilter(urlPatterns = "/attend/*",filterName = "channelFilter")

            第一个参数是需要修改成为你需要拦截的url路径,filterName是当前filter名称

3.启动类注册一下(配一下过滤器的路径)

 

4.最后切面类里直接拿参数

PS:可能有的人拿到的返回数据为空的,我重写的BodyReaderHttpServletRequestWrapper类里面有两个重载方法,一个是私有的,请求的时候调用public方法,这样就可以拿到数据.

5.这里面最大的坑:如果你都写好了还报空指针.去看一下Filter里@WebFilter(urlPatterns = "/attend/*",filterName = "channelFilter")

这个注解拦截的路径对不对.基本上是没拦住没有重新塞request造成的.

完毕 OVER ! ! !

 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果您的HttpServletRequest拦截器在读取一次请求数据之后再次读取时无法读取数据,可能是因为HttpServletRequest对象的输入流只能读取一次,如果您已经读取了它,那么它将不再可用。 为了解决这个问题,您可以将HttpServletRequest对象的请求数据读取到一个字节数组中,并将字节数组包装在一个新的HttpServletRequest对象中,然后将新的HttpServletRequest对象用于后续处理。 以下是一个示例: ```java public class MyInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 读取请求数据到字节数组中 byte[] requestBody = IOUtils.toByteArray(request.getInputStream()); // 创建新的HttpServletRequest对象,并将字节数组包装在里面 HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request) { @Override public ServletInputStream getInputStream() throws IOException { return new ServletInputStreamWrapper(requestBody); } @Override public int getContentLength() { return requestBody.length; } @Override public long getContentLengthLong() { return requestBody.length; } }; // 将新的HttpServletRequest对象用于后续处理 // ... return true; } } ``` 在这个示例中,我们使用了IOUtils.toByteArray()方法将HttpServletRequest对象的输入流读取到一个字节数组中。然后,我们创建了一个新的HttpServletRequestWrapper对象,并将字节数组包装在里面。最后,我们将新的HttpServletRequestWrapper对象用于后续处理。 这样,即使HttpServletRequest对象的输入流只能读取一次,您也可以在拦截器中多次读取HttpServletRequest对象的请求数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值