如何在 Spring Webflux Java 中记录请求正文

Posted

技术标签:

【中文标题】如何在 Spring Webflux Java 中记录请求正文【英文标题】:How to log request body in spring Webflux Java 【发布时间】:2020-08-25 15:37:23 【问题描述】:

我在 POST 请求中接收到一些 XML 有效负载,并希望看到收到的有效负载以进行调试。

下面(我的自定义 WebFilter)代码按预期记录 URI 和请求标头,但不记录请求正文/有效负载,我的反应代码一定有问题 -

       final ServerHttpRequest request = exchange.getRequest();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        LOGGER.info("Request: uri=", request.getURI());
        LOGGER.info("Request: headers=", request.getHeaders().entrySet());
        request.getBody().doOnNext(dataBuffer -> 
            try 
                Channels.newChannel(baos).write(dataBuffer.asByteBuffer().asReadOnlyBuffer());
                String body = new String(baos.toByteArray());
                LOGGER.info("Request: payload=", body);
             catch (IOException e) 
                e.printStackTrace();
             finally 
                try 
                    baos.close();
                 catch (IOException e) 
                    e.printStackTrace();
                
            
        );

我查看了 Brian 对下面帖子的回复并关注了它,但由于没有代码,我可能犯了一些愚蠢的错误

How to log request and response bodies in Spring WebFlux

更新代码

@Configuration
public class RequestFilter implements WebFilter 

    private static final Logger LOGGER = LoggerFactory.getLogger(RequestFilter.class);

    @Override
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public Mono<Void> filter(ServerWebExchange serverWebExchange,
                             WebFilterChain webFilterChain) 
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        serverWebExchange.getRequest().getBody().doOnNext(dataBuffer -> 
            try 
                Channels.newChannel(baos).write(dataBuffer.asByteBuffer().asReadOnlyBuffer());
                String body = new String(baos.toByteArray());
                LOGGER.info("Request: payload=", body);
             catch (IOException e) 
                e.printStackTrace();
             finally 
                try 
                    baos.close();
                 catch (IOException e) 
                    e.printStackTrace();
                
            
        );
        return webFilterChain.filter(serverWebExchange);
    

【问题讨论】:

你在这里打破链request.getBody().doOnNext( ... @ThomasAndolf - 你的意思是我需要在 doOn 之前对其进行平面映射?谢谢! 不,我的意思是您似乎忽略了返回值***.com/a/61541336/1840146 所以如果我 doOnNext().subscribe() 我得到了所需的请求正文,但是就像你在上面提到的答案中解释的那样,我没有可用的数据供进一步使用。我已经多次浏览了您的帖子,但找不到放置“返回 Mono.empty()”的正确位置。谢谢! 好吧,你显然以前从未使用过 webflux。这不是命令式编程。您需要始终保持联系,您不能只写request.getBody().doOnNext( ... 而忽略响应。请制作一个可重复的小示例,以便我了解更大的图景。你的小代码 sn -p 不够用。 【参考方案1】:

除了将尸体提取到错误的位置之外,您做的部分正确。您必须在 ServerHttpRequestDecorator 实现中执行此操作,并将其插入到 WebFilter 实现中。

public class RequestLoggingDecorator extends ServerHttpRequestDecorator 

  private static final Logger LOGGER = LoggerFactory.getLogger(RequestLoggingDecorator.class);

  public RequestLoggingDecorator(ServerHttpRequest delegate) 
    super(delegate);
  

  @Override
  public Flux<DataBuffer> getBody() 
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    return super.getBody().doOnNext(dataBuffer -> 
      try 
        Channels.newChannel(baos).write(dataBuffer.asByteBuffer().asReadOnlyBuffer());
        String body = new String(baos.toByteArray(), StandardCharsets.UTF_8);
        LOGGER.info("Request: payload=", body);
       catch (IOException e) 
        e.printStackTrace();
       finally 
        try 
          baos.close();
         catch (IOException e) 
          e.printStackTrace();
        
      
    );
  

然后您可以在WebFilter 实现中配置它,如下所示:

@Configuration
public class RequestLoggingFilter implements WebFilter 
  @Override
  public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) 
    ServerWebExchangeDecorator decorator =
        new ServerWebExchangeDecorator(serverWebExchange) 
          @Override
          public ServerHttpRequest getRequest() 
            return new RequestLoggingDecorator(serverWebExchange.getRequest());
          
        ;

    return webFilterChain.filter(decorator);
  

这应该在每个传入的 Http 请求中记录您的请求正文。

【讨论】:

如何在不使用 @Configuration 注释和自动配置的情况下以编程方式将这个 WebFilter 连接到 WebClient.Builder 中?

以上是关于如何在 Spring Webflux Java 中记录请求正文的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Spring WebFlux 获取“身份验证”对象?

如何在 Spring boot 2 + Webflux + Thymeleaf 中配置 i18n?

Kotlin结合Spring WebFlux实现响应式编程

如何在spring-webflux中获取当前请求的上下文

如何在 Spring Boot WebFlux 中使用 GET 请求注销

Spring WebFlux创建了无阻塞线程池