前言 在排查错误时通常都需要通过日志来查看接口的请求参数和响应结果来定位和分析问题,一般我们都会使用一个Filter
来做一些简单的请求日志记录,但是默认情况下 Spring Boot 是不支持记录请求体
和响应体
的,因为请求体和响应体都是以流的方式对外提供调用,如果在Filter
中把请求体和响应体读完了,就会使后续的应用读不到流数据导致异常。
实现思路 如果要记录请求体
和响应体
的话,需要将流使用完之后缓存在内存中,以供后续使用,这个实现起来好像还挺复杂,需要包装HttpServletRequest
、HttpServletResponse
两个类,然后对其中的IO
接口做处理,大概代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Bean public OncePerRequestFilter contentCachingRequestFilter () { return new OncePerRequestFilter () { @Override protected void doFilterInternal (final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException { CachingRequestWrapper wrappedRequest = new CachingRequestWrapper (request); CachingResponseWrapper wrappedResponse = new CachingResponseWrapper (response); filterChain.doFilter(wrappedRequest, wrappedResponse); LOGGER.info("http request:{}" , wrappedRequest.getContent()); LOGGER.info("http response:{}" , wrappedResponse.getContent()); } }; }
使用 spring 内置包装类 有了上面一步的思路应该可以实现记录请求体
和响应体
内容了,然而没必要,spring
官方已经提供了两个类来做这件事,就是ContentCachingRequestWrapper
和ContentCachingResponseWrapper
,使用方法也差不多,代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Bean public OncePerRequestFilter contentCachingRequestFilter () { return new OncePerRequestFilter () { @Override protected void doFilterInternal (final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException { ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper (request); ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper (response); filterChain.doFilter(wrappedRequest, wrappedResponse); LOGGER.info("http request:{}" , new String (wrappedRequest.getContentAsByteArray())); LOGGER.info("http response:{}" , new String (wrappedResponse.getContentAsByteArray())); wrappedResponse.copyBodyToResponse(); } }; }
附录 本文完整代码放在github 。