【Servlet】利用 HttpServletRequestWrapper 实现对request body的二次读取,可用于记录日志

        最近项目上有一个需求,在api上收到的请求,需要在springmvc转化成实体参数之前把request body读取出来记录日志。

        在通常的响应流程上,使用了request.getInputStream()之后,流就会失效,即这个request body的流只能读取一次,这也是流本身的特性所致(当然,还有一种特殊的流——推回输入流PushbackInputStream)。

       通过servlet api,可以通过继承 HttpServletRequestWrapper 这个类,在 filter 中 对 request 进行一次封装,再传进chain中,如此便可以实现日志的记录。

        首先是 HttpServletRequestWrapper 的子类:

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] bytes;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        try (BufferedInputStream bis = new BufferedInputStream(request.getInputStream());
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) > 0) {
                baos.write(buffer, 0, len);
            }
            bytes = baos.toByteArray();
            String body = new String(bytes);
            System.out.println(body);
        } catch (IOException ex) {
            throw ex;
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

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

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
    }

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

}

        接下来是filter的实现

@WebFilter(filterName = "httpServletRequestWrapperFilter", urlPatterns = "/*")
public class HttpServletRequestWrapperFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        if (request instanceof HttpServletRequest) {
            ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
            chain.doFilter(requestWrapper, response);
        } else {
            chain.doFilter(request, response);
        }

    }

    @Override
    public void destroy() {

    }


}

       此处是使用了spring boot的一个注解@WebFilter,在spring boot中,如果要扫描带@WebFilter的类,记得还要在Application入口类中加上@ServletComponentScan这个注解,才可以正常运行

猜你喜欢

转载自blog.csdn.net/woluoyifan/article/details/81280702