如何解决HttpServletRequest.getInputStream()多次读取问题

 更新时间:2023年07月31日 09:16:53   作者:yxl_num  
这篇文章主要介绍了如何解决HttpServletRequest.getInputStream()多次读取问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

HttpServletRequest.getInputStream()多次读取问题

使用POST方法发送数据时,我们习惯于把数据包装成json格式。

有些情况下,我们会在Filter中读取body数据进行数据校验,GET方法获取参数比较简单。

对于POST方法,可使用如下方法从request中获取body参数:

 private String getBody(HttpServletRequest request) throws IOException {
    InputStream in = request.getInputStream();
    BufferedReader br = new BufferedReader(new InputStreamReader(in, Charset.forName("UTF-8")));
    StringBuffer sb = new StringBuffer("");
    String temp;
    while ((temp = br.readLine()) != null) {
        sb.append(temp);
    }
    if (in != null) {
        in.close();
    }
    if (br != null) {
        br.close();
    }
    return sb.toString();
}

注意,这里有了一次request.getInputStream()调用。

但是在测试时,一直报JSON格式不正确的错误。经调查发现,项目中使用了公司基础组件中的Filter,而该Filter中也解析了body。

同时,不出所料,也是通过调用getInputStream()方法获取的。

原来:

  • 一个InputStream对象在被读取完成后,将无法被再次读取,始终返回-1;
  • InputStream并没有实现reset方法(可以重置首次读取的位置),无法实现重置操作;

因此,当自己写的Filter中调用了一次getInputStream()后,后面再调用getInputStream()读取的数据都为空,所以才报JSON格式不正确的错误。

解决方法

  • 缓存数据
  • 使用HttpServletRequestWrapper进行包装

缓存数据

所谓缓存数据,其实就是调用ServletRequestsetAttribute(String s, Object o)来存储数据。

1.获取到body后,直接缓存

String body = getBody(request);
request.setAttribute("body", body);

优点:

方便

缺点:

不能控制第三方Filter

1.其他地方需要使用body时,只需调用getAttribute方法就能获取数据了:

request.getAttribute("body");

HttpServletRequestWrapper包装

public class RequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;
    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = getBodyStringFromReq(request).getBytes(Charset.forName("UTF-8"));
    }
    public String getBodyString() {
        try {
            return new String(body, "UTF-8");
        } catch (UnsupportedEncodingException ex) {
            return new String(body);
        }
    }
    private String getBodyStringFromReq(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = 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();
    }
}

在Filter中使用时,FilterChain.doFilter()传入Wrapper对象:

public class TestFilter implements Filter {
    @Override
    public void destroy() {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest)request);
        String body = requestWrapper.getBodyString();
        chain.doFilter(requestWrapper, response);  //传入Wrapper对象
    }
    @Override
    public void init(FilterConfig arg0) throws ServletException {
    }
}

这样,位于后面的Filter就可以拥有唯一一次调用HttpServletRequest.getInputStream()的机会了。

优点:

不影响第三方Filter

缺点:

多写了这么多代码,麻烦了一些

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • java、freemarker保留两位小数

    java、freemarker保留两位小数

    这篇文章主要介绍了 java、freemarker保留两位小数的实现方法,然后给大家补充介绍了freemarker保留两位小数的详解,需要的朋友可以参考下
    2017-03-03
  • 详解Java如何实现加密或者解密PDF文档

    详解Java如何实现加密或者解密PDF文档

    PDF文档加密是一种用于保护文件内容的功能。这篇文章主要介绍了Java实现加密或者解密PDF文档的方法,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-03-03
  • java应用占用内存过高排查的解决方案

    java应用占用内存过高排查的解决方案

    这篇文章主要介绍了java应用占用内存过高排查的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-03-03
  • Java Stax解析XML示例

    Java Stax解析XML示例

    这篇文章主要介绍了Java Stax解析XML示例,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-09-09
  • java打印正弦曲线示例

    java打印正弦曲线示例

    这篇文章主要介绍了java数组排序示例,需要的朋友可以参考下
    2014-03-03
  • SpringBoot利用jpa连接MySQL数据库的方法

    SpringBoot利用jpa连接MySQL数据库的方法

    这篇文章主要介绍了SpringBoot利用jpa连接MySQL数据库的方法,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-10-10
  • Java中的函数式编程

    Java中的函数式编程

    这篇文章介绍Java中的函数式编程,函数式编程是一种编程范式,其中程序是通过应用和组合函数来构造的。它是一种声明式编程范式,其中函数定义是表达式树,每个表达式树返回一个值,而不是一系列改变程序状态的命令语句,具体情况请看下文,希望对你有所帮助
    2021-10-10
  • java 较大数据量取差集,list.removeAll性能优化详解

    java 较大数据量取差集,list.removeAll性能优化详解

    这篇文章主要介绍了java 较大数据量取差集,list.removeAll性能优化详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • spring boot整合Cucumber(BDD)的方法

    spring boot整合Cucumber(BDD)的方法

    本篇文章主要介绍了spring boot整合Cucumber(BDD)的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • Java单例模式中的线程安全问题

    Java单例模式中的线程安全问题

    本文主要介绍了Java单例模式中的线程安全问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06

最新评论