• 通过过滤器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
  • 相关阅读:
    Linux系统基础知识
    VGA设计(原理说明。Verilog代码实现,仿真结果)
    RK3568技术笔记 Ubuntu 安装VMware Tools
    TechTool Pro for mac中文激活版:硬件监测和系统维护工具
    sqlmap中文文档
    嵌入式开发需要具备什么技能才能在这个行业躺平
    安卓“灵动岛”,最新版来啦~
    reflow-回流 和 repaint-重绘
    6、堆(新生区,永久区,堆内存调优(jvm调优))
    力扣刷题学习SQL篇——1-6 删除(删除重复的电子邮箱——临时表实现/连表实现)
  • 原文地址:https://blog.csdn.net/Netbug_NB/article/details/126006744