Skip to content

问题描述

在工作中,我们需要通过过滤器对请求进行拦截,并记录请求的日志,在拦截请求时,我们可能会需要读取请求的json数据,但是通过request.getInputStream() 与 request.getReader() 只能调用一次,因为是流,读取完就没了。 然而,如在前端过滤器中,拦截请求数据并记录日志,记录日志后,请求继续向后传递,在后面的业务代码中,仍然需要继续取得请求的数据。

进过一顿搜索终于找到了解决方法,那就是重写HttpServletRequestWrapper,自己实现一个Wrapper。

解决方法

java

import lombok.Getter;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

@Getter
public class RequestWrapper extends HttpServletRequestWrapper {
    private final String body;
    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        StringBuilder sb = new StringBuilder();
        String line;
        BufferedReader reader = request.getReader();
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        body = sb.toString();
        //将post的body数据放入缓存,这样子在后面就能够随时取用
        request.setAttribute("body", body);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bais.read();
            }

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

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

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

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

}

在最前端的filter中,new一个自己的wrapper,并doChain继续向后传递即可。

java
import cn.hutool.core.util.StrUtil;
import com.kelebb.utilities.requests.RequestWrapper;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * 此过滤器用途
 * 因为Request的请求方式为JSON时,参数流只能读取一次
 * 可能导致在过滤器、拦截器、AOP做处理后无法正常获取参数
 * 需在此做处理
 */
@Component
@Order(1)
public class MultiReadHttpServletRequestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (request instanceof HttpServletRequest httpServletRequest) {
            String contentType = httpServletRequest.getHeader("Content-Type");
            // 只对json参数做处理
            if (StrUtil.isNotEmpty(contentType) && contentType.contains("application/json")) {
                RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
                chain.doFilter(requestWrapper, response);
            }else {
                chain.doFilter(request, response);
            }
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}