• 转载-Spring Boot之 Filter(示例:打印request、response日志)


    网上有很多采用spring filter机制打印request/response日志的博客, 大都不能很好工作, 下面这个博客写的不错.

    https://blog.csdn.net/jy02268879/article/details/84243950

    作者用到了下面两个第三方库, 其中 apache lang3 的 StringUtils 可以使用 Hutool 库代替.  jodd 库是一个非常优秀的工具包.

    • import jodd.io.StreamUtil;
    • import org.apache.commons.lang3.StringUtils;

    pom.xml 引入对应的 jodd-core 包.

                <dependency>
                    <groupId>org.jodd</groupId>
                    <artifactId>jodd-core</artifactId>
                    <version>${jodd.all.version}</version>
                </dependency>

    下面内容摘自  https://blog.csdn.net/jy02268879/article/details/84243950

    ===========================================

    Filter(过滤器)

    ===========================================
    一个请求可以被多个过滤器拦截到,会依次进入各个Filter中,放行后直至进入Servlet,Servlet处理请求结束后,回到各个Filter继续执行后面的代码,先执行的Filter,后执行完(Filter是个栈结构,先进后出)。

    例如:这里有5个filter: A,B,C,D,E

    执行filter的前置处理的顺利是A,B,C,D,E

    那么执行filter的后置处理的顺序是E,D,C,B,A

    一个请求进来以后的执行顺序:

    Filter前置处理---->Interceptor(拦截器)前置处理---->正常的controller处理---->Interceptor后置处理---->Filter后置处理

    ===========================================
    一.用@WebFilter注册过滤器

    ===========================================

    ---------------------------------------------------------
     1.实现filter接口,或者继承filter的实现类, 

    ---------------------------------------------------------

    RequestFilter.java 继承OncePerRequestFilter确保一次请求只通过一次该filter

    换言之一次请求不会通过两次RequestFilter,一次请求不会重复执行自定义RequestFilter中的doFilterInternal方法

    package com.sid.util.LogRequestResponse;
     
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.annotation.Order;
    import org.springframework.web.filter.OncePerRequestFilter;
     
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.BufferedReader;
    import java.io.IOException;
     
    /**
     * @program: springboot
     * @description:
     * @author: Sid
     * @date: 2018-11-19 09:21
     * @since: 1.0
     **/
    @Order(0)
    /**
     * 注册过滤器
     * */
    @WebFilter(filterName = "RequestResponseLogFilter", urlPatterns = "/*")
    public class RequestFilter extends OncePerRequestFilter {
        private static final Logger logger = LoggerFactory.getLogger(RequestFilter.class);
     
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            String path = request.getQueryString();
            String servletPath = request.getServletPath();
            String url = request.getRequestURI();
            RequestWrapper requestWrapper = null;
     
     
            StringBuilder sb = new StringBuilder();
            if (request instanceof HttpServletRequest) {
                requestWrapper = new RequestWrapper(request);
                    BufferedReader bufferedReader = requestWrapper.getReader();
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        sb.append(line);
                    }
            }
     
            ResponseWrapper responseWrapper=new ResponseWrapper( response);
     
            if (null == requestWrapper) {
                filterChain.doFilter(request, response);
            } else {
                filterChain.doFilter(requestWrapper, responseWrapper);
            }
            logger.info("========================》  url:" + url + " & queryString:" + path+" & servletPath:"+servletPath);
            logger.info("========================》request uri: {}",request.getRequestURI());
            logger.info("========================》request ContentType: {}",request.getContentType());
            logger.info("========================》request param: {}",sb.toString());
     
            logger.info("========================》response status: {}",response.getStatus());
            logger.info("========================》response ContentType: {}",response.getContentType());
     
     
            String result=new String(responseWrapper.getResponseData());
            ServletOutputStream outputStream = response.getOutputStream();
            outputStream.write(result.getBytes());
            outputStream.flush();
            outputStream.close();
            // 打印response
            logger.info("========================》response return data: {} 	" + result);
     
        }
     
    } 



    ---------------------------------------------------------
     2.在spring-boot启动类上加注解@ServletComponentScan
     ---------------------------------------------------------
     

    @SpringBootApplication
    @ServletComponentScan
    public class App {
        public static void main(String[] args) {
            SpringApplication.run(App.class, args);
        }
    }


     ===========================================
     二、用FilterRegistrationBean注册过滤器

    ===========================================

    RequestFilterConfiguration.java

    package com.sid.util.LogRequestResponse;
     
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
     
    /**
     * @program: springboot
     * @description:
     * @author: Sid
     * @date: 2018-11-19 13:48
     * @since: 1.0
     **/
    @Configuration
    public class RequestFilterConfiguration {
        @Bean
        public FilterRegistrationBean authFilterRegistrationBean() {
            FilterRegistrationBean registration = new FilterRegistrationBean();
            registration.setFilter(new RequestFilter()); //设置自定义的Filter
            registration.addUrlPatterns("/*");  //设置过滤路径
            registration.setName("RequestFilter"); //设置过滤器名称
            registration.setOrder(1);   //设置过滤器顺序
            //registration.addInitParameter("paramName", "paramValue");  //设置初始化参数 这里不用
            return registration;
        }
    } 


    ----------------------------------------------------
    RequestWrapper的实现

    ----------------------------------------------------

    package com.sid.util.LogRequestResponse;
     
    import jodd.io.StreamUtil;
    import org.apache.commons.lang3.StringUtils;
     
    import java.io.*;
    import java.nio.charset.Charset;
    import java.util.Enumeration;
     
    import javax.servlet.ReadListener;
    import javax.servlet.ServletInputStream;
    import javax.servlet.ServletRequest;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
     
     
    /**
     * @program: springboot
     * @description:
     * @author: Sid
     * @date: 2018-11-19 12:54
     * @since: 1.0
     **/
    public class RequestWrapper extends HttpServletRequestWrapper {
        private final byte[] body;
     
        /**
         * 这个必须加,复制request中的bufferedReader中的值
         * @param request
         * @throws IOException
         */
        public RequestWrapper(HttpServletRequest request) throws IOException {
            super(request);
            body = getBodyString(request);
        }
     
        /**
         * 获取请求Body
         *
         * @param request
         * @return
         */
        public byte[] getBodyString(final ServletRequest request) throws IOException {
            String contentType = request.getContentType();
            String bodyString ="";
     
            if (StringUtils.isNotBlank(contentType) && (contentType.contains("multipart/form-data") || contentType.contains("x-www-form-urlencoded"))){
     
                Enumeration<String> pars=request.getParameterNames();
     
                while(pars.hasMoreElements()){
     
                    String n=pars.nextElement();
     
                    bodyString+=n+"="+request.getParameter(n)+"&";
     
                }
     
                bodyString=bodyString.endsWith("&")?bodyString.substring(0, bodyString.length()-1):bodyString;
     
                return bodyString.getBytes(Charset.forName("UTF-8"));
     
            }else {
     
                return StreamUtil.readBytes(request.getReader(), "UTF-8");
     
            }
        }
     
     
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(getInputStream()));
        }
     
        @Override
        public ServletInputStream getInputStream() throws IOException {
            final ByteArrayInputStream bais = new ByteArrayInputStream(body);
            return new ServletInputStream() {
     
                @Override
                public boolean isFinished() {
                    return false;
                }
     
                @Override
                public boolean isReady() {
                    return false;
                }
     
                @Override
                public void setReadListener(ReadListener listener) {
     
                }
     
                @Override
                public int read() throws IOException {
                    return bais.read();
                }
            };
        }
    } 

    ----------------------------------------------------
    ResponseWrapper的实现

    ----------------------------------------------------

    package com.sid.util.LogRequestResponse;
     
    import javax.servlet.WriteListener;
    import javax.servlet.http.HttpServletResponseWrapper;
     
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.io.UnsupportedEncodingException;
     
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
     
    /**
     * @program: springboot
     * @description:
     * @author: Sid
     * @date: 2018-11-19 11:55
     * @since: 1.0
     **/
    public class ResponseWrapper extends HttpServletResponseWrapper {
        /**
         * This class implements an output stream in which the data is written into a byte array.
         * The buffer automatically grows as data is written to it. The data can be retrieved using toByteArray() and toString().
         Closing a ByteArrayOutputStream has no effect. The methods in this class can be called after the stream has been closed without generating an IOException.
         */
        private ByteArrayOutputStream buffer = null;//输出到byte array
        private ServletOutputStream out = null;
        private PrintWriter writer = null;
     
        public ResponseWrapper(HttpServletResponse resp) throws IOException {
            super(resp);
            buffer = new ByteArrayOutputStream();// 真正存储数据的流
            out = new WapperedOutputStream(buffer);
            writer = new PrintWriter(new OutputStreamWriter(buffer, this.getCharacterEncoding()));
        }
     
        /** 重载父类获取outputstream的方法 */
        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            return out;
        }
     
        /** 重载父类获取writer的方法 */
        @Override
        public PrintWriter getWriter() throws UnsupportedEncodingException {
            return writer;
        }
     
        /** 重载父类获取flushBuffer的方法 */
        @Override
        public void flushBuffer() throws IOException {
            if (out != null) {
                out.flush();
            }
            if (writer != null) {
                writer.flush();
            }
        }
     
        @Override
        public void reset() {
            buffer.reset();
        }
     
        /** 将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据 */
        public byte[] getResponseData() throws IOException {
            flushBuffer();
            return buffer.toByteArray();
        }
     
        /** 内部类,对ServletOutputStream进行包装 */
        private class WapperedOutputStream extends ServletOutputStream {
            private ByteArrayOutputStream bos = null;
     
            public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException {
                bos = stream;
            }
     
            @Override
            public void write(int b) throws IOException {
                bos.write(b);
            }
     
            @Override
            public void write(byte[] b) throws IOException {
                bos.write(b, 0, b.length);
            }
     
            @Override
            public boolean isReady() {
                return false;
            }
     
            @Override
            public void setWriteListener(WriteListener listener) {
     
            }
        }
    } 
  • 相关阅读:
    Java秒杀实战 (三)秒杀基本功能开发
    Java优化高性能高并发+高并发程序设计视频教程
    Java远程通讯可选技术及原理
    SCRUM MASTER检查单
    每日站会
    “完成”的定义
    Sprint
    敏捷估算
    VerbalExpressions ——另类正则表达式
    几篇关于VisualStudio的调试工具文章
  • 原文地址:https://www.cnblogs.com/harrychinese/p/SpringBoot_Filter.html
走看看 - 开发者的网上家园