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

相关推荐
快乐就好ya29 分钟前
Java多线程
java·开发语言
IT学长编程34 分钟前
计算机毕业设计 二手图书交易系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·计算机毕业设计选题·二手图书交易系统
CS_GaoMing1 小时前
Centos7 JDK 多版本管理与 Maven 构建问题和注意!
java·开发语言·maven·centos7·java多版本
艾伦~耶格尔1 小时前
Spring Boot 三层架构开发模式入门
java·spring boot·后端·架构·三层架构
麻辣韭菜1 小时前
网络基础 【HTTP】
网络·c++·http
man20172 小时前
基于spring boot的篮球论坛系统
java·spring boot·后端
2401_858120532 小时前
Spring Boot框架下的大学生就业招聘平台
java·开发语言
S hh2 小时前
【Linux】进程地址空间
java·linux·运维·服务器·学习
Java探秘者2 小时前
Maven下载、安装与环境配置详解:从零开始搭建高效Java开发环境
java·开发语言·数据库·spring boot·spring cloud·maven·idea
攸攸太上2 小时前
Spring Gateway学习
java·后端·学习·spring·微服务·gateway