• HttpServletRequest.getInputStream()多次读取问题


    转自:https://www.jianshu.com/p/85feeb30c1ed

    HttpServletRequest.getInputStream()多次读取问题

     

    背景

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

     
    image.png

    有些情况下,我们会在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格式不正确的错误。

    解决方法

    1. 缓存数据
    2. 使用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

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

    参考

  • 相关阅读:
    MySQL mysqlbinlog 读取mysql-bin文件出错
    MySQL slow_log表不能修改成innodb引擎
    Linux 进程一直占用单核CPU分析
    Linux 磁盘告警分析
    Linux 查看文件被那个进程写数据
    springboot项目访问jsp404
    springboot项目启动控制台显示端口被占用解决方法
    js密码强弱正则校验、邮箱校验
    Java Base64加密解密例子
    mysql按日期做曲线图统计,如果当天没有数据则日期不全、补全日期
  • 原文地址:https://www.cnblogs.com/drafire/p/11937578.html
Copyright © 2020-2023  润新知