Java过滤器

通过实现Filter接口,重写doFilter方法做增强操作。 Filter 并不是一个标准的Servlet ,它不能处理用户请求,也不能对客户端生成响应。 主要用于对HttpServletRequest 进行预处理,也可以对HttpServletResponse 进行后处理,是个典型的处理链 Filter有三个方法init、doFilter、destroy,对应请求初始化时、执行时和销毁时,主要在doFilter里面做文章 doFilter方法三个参数,请求体,响应体,过滤器链 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;

案例:给所有请求增加日志信息打印(入参、出参)

@Component
//自定义一个过滤器实现Filter接口、配置@WebFilter注解,配置拦截路径
@WebFilter(filterName = "myFilter", urlPatterns = {"/*"})
@Slf4j
public class RequestFilter implements Filter {

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

    }

    @SneakyThrows
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {

        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String requestId = httpServletRequest.getHeader("requestId");
        if(StringUtil.isBlank(requestId)){
            requestId = UUID.randomUUID().toString().replaceAll("-", "");
        }
        MDC.put("requestId", requestId);
            //这里只针对application/json 进行日志打印
        if (httpServletRequest.getContentType() != null && httpServletRequest.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {
            //转换成代理类(代理类作用是为了拿到请求头和响应体的内容)
            ResponseWrapper wrapperResponse = new ResponseWrapper(httpServletResponse);
            RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
            long before = System.currentTimeMillis();
            // 打印请求 url,ip
            log.info("请求IP:{}, URL:{},", httpServletRequest.getRemoteAddr(), httpServletRequest.getRequestURL().toString());
            log.info("请求参数:{}", JSON.toJSONString(httpServletRequest.getParameterMap()));
            // 打印请求入参
            log.info("请求体:{} ", JSON.toJSONString(requestWrapper.getBody()));
            filterChain.doFilter(requestWrapper, wrapperResponse);
            //获取返回值
            byte[] content = wrapperResponse.getContent();
            // 打印出参
            log.info("返回结果: " + JSON.toJSON(new String(content)));
            // 执行耗时
            log.info("耗时: " + (System.currentTimeMillis() - before) + " ms");
            MDC.remove("requestId");
            //把返回值输出到客户端
            ServletOutputStream out = httpServletResponse.getOutputStream();
            out.write(content);
            out.flush();
        } else {
            filterChain.doFilter(servletRequest, servletResponse);
            MDC.remove("requestId");
        }
    }
}

FilterChain 接口,该接口由容器实现,其实它的作用是一个回调接口,它只有一个方法doFilter。可以定义很多个Filter,都会执行

代码里有个东西叫MDC

MDC是什么?能干什么?

是什么? MDC 全称是 Mapped Diagnostic Context,可以粗略的理解成是一个线程安全的存放诊断日志的容器。 能干什么? 在Web应用中,如果想在日志中输出请求用户IP地址、请求URL、统计耗时等,MDC基本都能支撑; 在Web应用中,如果能画出用户的请求到响应整个过程,势必会快速定位生产问题,那么借助 MDC 来保存用户请求时产生的 requestId,当请求完成后,再将这个 requestId 进行移除,这么一来通过 grep requestId 就能轻松查出整个请求流程的日志轨迹。

另,请求和响应的代理类,封装类

public class RequestWrapper extends HttpServletRequestWrapper {

    private final String body;

    public RequestWrapper(HttpServletRequest request) {
        super(request);
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {

        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        body = stringBuilder.toString();
    }

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

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

            @Override
            public void setReadListener(ReadListener readListener) {
            }

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

    }

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

    public String getBody() {
        return this.body;
    }

}
public class ResponseWrapper extends HttpServletResponseWrapper {

    private ByteArrayOutputStream buffer;

    private ServletOutputStream out;

    public ResponseWrapper(HttpServletResponse httpServletResponse) {
        super(httpServletResponse);
        buffer = new ByteArrayOutputStream();
        out = new WrapperOutputStream(buffer);
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return out;
    }

    @Override
    public void flushBuffer() throws IOException {
        if (out != null) {
            out.flush();
        }
    }

    public byte[] getContent() throws IOException {
        flushBuffer();
        return buffer.toByteArray();
    }

    class WrapperOutputStream extends ServletOutputStream {
        private ByteArrayOutputStream bos;

        public WrapperOutputStream(ByteArrayOutputStream bos) {
            this.bos = bos;
        }

        @Override
        public void write(int b) throws IOException {
            bos.write(b);
        }

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

        @Override
        public void setWriteListener(WriteListener arg0) {

        }
    }
}