java打印http接口的请求和响应

打印http接口的请求和响应

一、简述

基于spring提供的机制,有3种方法可以实现接口请求响应日志的打印,分别是CommonsRequestLoggingFilter、HandlerInterceptor、RequestBodyAdviceAdapter、ResponseBodyAdvice。

二、修改日志级别打印请求参数

通过设置 web 的日志级别为 DEBUG,spring会自己打印请求参数。该方法打印的内容覆盖了后面介绍的所有方法中日志的内容,如果不需要做定制打印,并且不介意打印的日志级别是DEBUG,那就足够用了。

yml 复制代码
logging:
  level:
    root: INFO
    web: DEBUG

三、使用 CommonsRequestLoggingFilter 打印请求参数

CommonsRequestLoggingFilter的使用比较简单,只需要实现一个logFilter的bean即可。

只不过logFilter的日志级别是debug,需要在日志配置文件中,将CommonsRequestLoggingFilter类的日志级别设置为debug级别。

同时在生产环境的日志文件中打印debug日志不符合规范。

java 复制代码
@Bean
public CommonsRequestLoggingFilter logFilter() {
    CommonsRequestLoggingFilter loggingFilter = new CommonsRequestLoggingFilter();

    loggingFilter.setIncludeQueryString(true);
    loggingFilter.setIncludePayload(true);
    loggingFilter.setMaxPayloadLength(2048);

    return loggingFilter;
}

四、使用 HandlerInterceptor 打印请求参数

HandlerInterceptor 可以获取到接口执行过程中的 HttpServletRequest 和 HttpServletResponse 信息,因此能够打印出接口请求响应内容。

java 复制代码
@Component
public class LogInterceptorAdapter extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) {

        ServletRequest servletRequest = new ContentCachingRequestWrapper(request);
        Map<String, String[]> params = servletRequest.getParameterMap();

        // 从 request 中读取请求参数并打印
        params.forEach((key, value) -> log.info("logInterceptor " + key + "=" + Arrays.toString(value)));
        // 避免从 inputStream 中读取body并打印

        return true;
    }
}

这种方式有个缺陷,对于 application/json 这种请求参数放在body中的方式,需要通过InputStream读取内容,而InputStream只能被读取一次,

一旦在 HandlerInterceptor 中进行了 InputStream 的读取操作,后续的处理就读取不到InputStream中的内容,这是一个很严重的问题。

因此 HandlerInterceptor 不能用于打印请求中的body,可以改造一下该方法,只打印get请求参数,post的请求参数用下面介绍的 RequestBodyAdviceAdapter 方法打印。

java 复制代码
@Slf4j
@Component
public class LogInterceptorAdapter extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) {
        if (DispatcherType.REQUEST.name().equals(request.getDispatcherType().name())
                && request.getMethod().equals(HttpMethod.GET.name())) {

            ServletRequest servletRequest = new ContentCachingRequestWrapper(request);
            Map<String, String[]> params = servletRequest.getParameterMap();

            // 从 request 中读取请求参数并打印
            params.forEach((key, value) -> log.info("logInterceptor " + key + "=" + Arrays.toString(value)));
            // 避免从 inputStream 中读取body并打印

        }
        return true;
    }
}

五、使用 RequestBodyAdviceAdapter 打印请求参数

RequestBodyAdviceAdapter 封装了 afterBodyRead 方法,在这个方法中可以通过 Object body 参数获取到body的内容。

java 复制代码
@ControllerAdvice
public class CustomRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {

    @Autowired
    HttpServletRequest httpServletRequest;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, 
                            Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage,
                                MethodParameter parameter, Type targetType,
            Class<? extends HttpMessageConverter<?>> converterType) {

        // 打印body内容

        return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
    }
}

六、使用 ResponseBodyAdvice 打印响应内容

ResponseBodyAdvice 和 RequestBodyAdviceAdapter 同属于 ControllerAdvice。ResponseBodyAdvice 封装了 beforeBodyWrite 方法,可以获取到响应报文。

java 复制代码
@ControllerAdvice
public class CustomResponseBodyAdviceAdapter implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter methodParameter,
                            Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter methodParameter,
                                  MediaType mediaType,
                                  Class<? extends HttpMessageConverter<?>> aClass,
                                  ServerHttpRequest serverHttpRequest,
                                  ServerHttpResponse serverHttpResponse) {

        if (serverHttpRequest instanceof ServletServerHttpRequest &&
                serverHttpResponse instanceof ServletServerHttpResponse) {
            // 打印响应body
        }

        return body;
    }
}

七、使用 filter 打印请求和响应

通过继承spring的 OncePerRequestFilter实现自定义filter。在filter中读取请求和响应的body需要做一下特殊处理,因为流只能被读取一次,在filter中被读取了,后续的处理就无法再次读取流的内容了。

spring提供了 ContentCachingRequestWrapper和 ContentCachingResponseWrapper两个类来解决这个问题。

java 复制代码
@Slf4j
@Component
public class AccessLogFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        ContentCachingRequestWrapper req = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper resp = new ContentCachingResponseWrapper(response);

        try {
            // Execution request chain
            filterChain.doFilter(req, resp);
            // Get body
            byte[] requestBody = req.getContentAsByteArray();
            byte[] responseBody = resp.getContentAsByteArray();
        
            log.info("request body = {}", new String(requestBody, StandardCharsets.UTF_8));
            log.info("response body = {}", new String(responseBody, StandardCharsets.UTF_8));
        } finally {
        // Finally remember to respond to the client with the cached data.
            resp.copyBodyToResponse();
        }    
    }
}

原文链接:https://blog.csdn.net/haiyan_qi/article/details/109960325

相关推荐
一个有女朋友的程序员几秒前
Spring Boot 整合 Smart-Doc:零注解生成 API 文档,告别 Swagger
java·spring boot·smart-doc
苹果醋321 分钟前
AI大模型竞赛升温:百度发布文心大模型4.5和X1
java·运维·spring boot·mysql·nginx
网安INF25 分钟前
CVE-2020-1938源码分析与漏洞复现(Tomcat 文件包含/读取)
java·网络·web安全·网络安全·tomcat·漏洞复现
nenchoumi311937 分钟前
UE5 学习系列(九)光照系统介绍
java·学习·ue5
张乔241 小时前
spring boot项目整合mybatis实现多数据源的配置
java·spring boot·多数据源
GzlAndy1 小时前
Tomcat调优
java·tomcat
美好的事情能不能发生在我身上1 小时前
苍穹外卖Day11代码解析以及深入思考
java·spring boot·后端·spring·架构
辉辉健身中1 小时前
Maven入门(够用)
java·maven
星火飞码iFlyCode1 小时前
【无标题】
java·前端·人工智能·算法
不良手残2 小时前
Redisson + Lettuce 在 Spring Boot 中的最佳实践方案
java·spring boot·redis·后端