【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这个注解,才可以正常运行

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
当在拦截器中需要对POST请求的参数进行处理时,有时会出现"Required request body is missing"异常。这是因为POST请求中的参数以流的形式存在,流数据只能读取一次。为了解决这个问题,可以使用HttpServletRequestWrapper来包装HttpServletRequest,这样可以将流保存起来,使参数能够多次读取。通过实现一个自定义的HttpServletRequestWrapper类,我们可以在拦截器中对请求参数进行处理,避免出现参数丢失的异常。 下面是一个示例拦截器的代码: ```java @Order(1) @WebFilter(filterName="koalaSignFilter",urlPatterns="/*") public class KoalaSignFilter implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { // do nothing } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletRequest requestWrapper=null; if(request instanceof HttpServletRequest) { requestWrapper=new KoalaHttpRequestWrapper((HttpServletRequest)request); } if(requestWrapper==null) { chain.doFilter(request, response); }else { chain.doFilter(requestWrapper, response); } } @Override public void destroy() { // do nothing } } ``` 在这个示例中,我们通过KoalaHttpRequestWrapper类来包装HttpServletRequest,并将包装后的request对象传递给FilterChain进行处理。这样就能够解决"Required request body is missing"异常问题。 总结来说,解决"Required request body is missing"拦截器的方法是通过使用HttpServletRequestWrapper包装HttpServletRequest,使得参数流能够多次读取。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [springboot-拦截器-过滤器-Required request body is missing 异常](https://blog.csdn.net/qq_33517683/article/details/78593487)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值