• 通过过滤器Filter实现请求响应报文的日志输出


    背景知识

    项目组需要打印所有Restful接口的请求及响应报文,而通过HttpServletRequest获取输入流InputStream最多只能获取一次,重复获取会抛异常——Required Request Body is Missing。

    相关技术知识点:

    接口/类描述主要方法
    javax.servlet.Filter接口获取请求Bytes报文;组装RequestWrapper、ResponseWrapper;获取响应Bytes报文;回写Response;public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain filterChain) throws IOException, ServletException
    javax.servlet.ServletInputStream接口自定义InputStream,通过ByteArrayInputStream为后续逻辑提供read()方法,可以形象化理解为请求体的Getter方法;public int read() throws IOException
    javax.servlet.ServletOutputStream接口自定义OutputStream,通过ByteArrayOutputStream为前置逻辑提供write方法,可以形象化理解为响应体的Setter方法;public void write(int b) throws IOException
    javax.servlet.http.HttpServletRequestWrapper接口自定义ServletRequest,获取自定义InputStreampublic ServletInputStream getInputStream() throws IOException
    javax.servlet.http.HttpServletResponseWrapper接口自定义ServletResponse,获取自定义OutputStreampublic ServletOutputStream getOutputStream() throws IOException
    java.io.ByteArrayInputStream已byte[]为参构造InputStreampublic ByteArrayInputStream(byte buf[])
    java.io.ByteArrayOutputStream从OutputStream中获取byte[]public synchronized byte toByteArray()[]

    调用时许(待完善):

    Filter ServletWrapper ServletStream ByteArrayStream ServletRequestWrapper.getInputStream ServletInputStream.read ByteArrayInputStream(byte buf[]) ByteArrayStream之Input、Output ServletOutputStream.write ServletResponseWrapper.getOutputStream ByteArrayOutputStream.toByteArray Filter ServletWrapper ServletStream ByteArrayStream 请求、响应报文体打印输出

    编码实现

    • BodyLogInputStream.java
    public class BodyLogInputStream extends ServletInputStream {
    	private final ByteArrayInputStream is;
    	public BodyLogInputStream(byte[] body) {
    		is = new ByteArrayInputStream(body);
    	}
    	@Override
    	public boolean isFinished() {
    		return true;
    	}
    	@Override
    	public boolean isReady() {
    		return true;
    	}
    	@Override
    	public void setReadListener(ReadListener readListener){
    		
    	}
    	@Override
    	public int read() throws IOException {
    		return is.read();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • BodyLogOutputStream.java
    public class BodyLogOutputStream extends ServletOutputStream {
    	private final ByteArrayOutputStream os;
    	public BodyLogOutputStream(ByteArrayOutputStream os){
    		this.os = os;
    	}
    	@Override
    	public boolean isReady() { return true; }
    	@Override
    	public void setWriteListener(WriteListener writeListener){ }
    	@Override
    	public void write(int b) throws IOException {
    		os.write(b);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • BodyLogRequestWrapper.java
    public class BodyLogRequestWrapper extends HttpServletRequestWrapper {
    	private final byte[] body;
    	public BodyLogRequestWrapper(HttpServletRequest request, byte[] body){
    		super(request);
    		this.body = body;
    	}
    	@Override
    	public ServletInputStream getInputStream() throw IOException {
    		return new BodyLogInputStream(data);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • BodyLogResponseWrapper.java
    public class BodyLogResponseWrapper extends HttpServletResponseWrapper {
    	private final ByteArrayOutputStream os;
    	public BodyLogResponseWrapper (HttpServletResponse response){
    		super(response);
    		os = new ByteArrayOutputStream();
    	}
    	@Override
    	public ServletOutputStream getOutputStream() throws IOException {
    		return new BodyLogOutputStream(os);
    	}
    	/**
    	* 钩子函数 获取写入OutputStream中的字节数组
    	*/
    	public byte[] getBytes() throws IOException {
    		return BodyLogUtil.getBytes(os);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • BodyLogUtil.java
    public class BodyLogUtil {
    	public static byte[] getBytes(ByteArrayOutputStream os) throws IOException {
    		os.flush();
    		byte[] bytes = os.toByteArray();
    		os.close();
    		return bytes;
    	}
    	
    	public static byte[] getBytes(InputStream is) throws IOException{
    		ByteArrayOutputStream os = new ByteArrayOutputStream();
    		byte[] buffer = new byte[1024];
    		int n = -1;
    		while( -1 != (n = is.read(buffer))) {
    			os.write(buffer, 0, n);
    		}
    		is.close();
    		return getBytes(os);
    	}
    	public static void setBytes(OutputStream os,byte[] bytes) throws IOException{
    		os.write(bytes);
    		os.close();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • BodyLogFilter.java
    public class BodyLogFilter implements Filter {
    	private final static Logger logger = LoggerFactory.getLogger(BodyLogFilter.class);
    	@Override
    	public void init(FilterConfig filterConfig) throws ServletException {
    		
    	}
    	@Override
    	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    		//获取请求报文
    		byte[] requestData = BodyLogUtil.getBytes(servletRequest.getInputStream());
    		logger.info("请求报文:{}",new String(requestData,Charset.forName(servletRequest.getCharacterEncoding())));
    	
    		//组装RequestWrapper与ResponseWrapper
    		BodyLogRequestWrapper requestWrapper = new BodyLogRequestWrapper((HttpServletReuqest)servletRequest, requestData);
    		BodyLogResponseWrapper responseWrapper = new BodyLogResponseWrapper((HttpServletResponse)servletResponse);
    		filterChain.doFilter(requestWrapper, responseWrapper);
    		
    		//获取响应报文
    		byte[] responseData = responseWrapper.getBytes();
    		logger.info("响应报文:{}",new String(responseData,Charset.forName(servletRequest.getCharacterEncoding())));
    		
    		//回写Reponse
    		BodyLogUtil.setBytes(servletResponse.getOutputStream(), responseData);
    
    	}
    
    	@Override
    	publiv void destroy() {
    		
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
  • 相关阅读:
    hero博客搭建
    【经验】Word 2021|如何在Word里做出和Markdown中一样漂亮的引用样式(结尾附成品)
    22-07-19 西安 RabbitMQ(03) 消息可靠投递、消费端限流、死信队列、延迟队列、集群搭建
    【RocketMQ系列九】SpringCloudStream整合RocketMQ
    如何使用 DataAnt 监控 Apache APISIX
    Java中的多线程如何理解——精简
    java2022最新面试题及答案,轻松过秋招
    Vuex全篇
    部署SpringBoot+SpringCloud+Vue项目——半途而废版
    SpringSecurity Oauth2实战 - 03 内存数据源完成认证登录
  • 原文地址:https://blog.csdn.net/Netbug_NB/article/details/126006744