侧边栏壁纸
博主头像
小白博主等级

just do it!

  • 累计撰写 60 篇文章
  • 累计创建 77 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

记录一次Filter中读取request body参数调试(getReader() has already been called、request body is missing)

小白
2021-01-14 / 0 评论 / 0 点赞 / 85 阅读 / 452 字

现象

filter中获取HttpServletRequest 请求Body中的数据后,报错:getReader() has already been called、request body is missing。

根本原因

ServletRequest输入流只可以获取一次:stream对应数据,数据存储在内存或者部分存储在内存中,在对stream操作时,每read一次mark一次当前位置,第二次read留时就从mark的位置继续读数据(从内存中copy),因此stream读取一次后再次读取为空。

如何避免?更改stream中mark的位置即pos改为0。Java中reset()方法可以重置streammark位置为其实位置,注意:并不是所有的stream都可以调用reset()方法,ServletInputStream恰恰不可以调用reset方法,因此ServletRequest中流只可以调取一次。

解决方案

新建类继承HttpServletRequestWrapper,类中新建对象存储body数据。filter中使用时,使用新建对象类:

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;
    private String bodyStr;

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

    public String getBodyStr() {
        return bodyStr;
    }

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

        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }

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

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

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


    public  String getBodyString(HttpServletRequest request) throws IOException {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(
                    new InputStreamReader(inputStream, Charset.forName("UTF-8")));

            char[] bodyCharBuffer = new char[1024];
            int len = 0;
            while ((len = reader.read(bodyCharBuffer)) != -1) {
                sb.append(new String(bodyCharBuffer, 0, len));
            }
        } 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中使用

public class filter extends OncePerRequestFilter{
    ...
    
    protected void doFilterInternal(HttpServletRequest request,HttpServletResponse respnose,FilterChain filterChain) throws ServletException,IOException{
        BodyReaderHttpServletRequestWrapper wrapper = new BodyReaderHttpServletRequestWrapper(request);
        wrapper.getBodyStr();//获取body json字符串
        filterChain.doFilter(wrapper,response);
    }

    ...
}
0

评论区