转载-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: {} \t" + 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) { } } }