You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring WebFlux应用中仅记录Spring HttpClient的HTTP 400错误响应

处理Spring WebFlux中WebClient返回HTTP 400时的响应记录

要实现WebClient调用目标URL返回HTTP 400状态码时记录响应内容,你可以通过以下方式修改现有代码,核心思路是在处理ClientResponse时,先判断状态码,缓存响应体后完成日志记录,再继续构建返回给客户端的响应:

修改后的完整代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public class UrlHandler {
    // 初始化SLF4J日志实例
    private static final Logger log = LoggerFactory.getLogger(UrlHandler.class);
    private WebClient webClient;

    public UrlHandler(WebClient webClient) {
        this.webClient = webClient;
    }

    public Mono<ServerResponse> fetch(ServerRequest request) {
        String u = request.queryParam("u").get();
        return webClient
                .get()
                .uri(u)
                .exchangeToMono(UrlHandler::fromClientResponse)
                .retry(1);
    }

    private static Mono<ServerResponse> fromClientResponse(final ClientResponse clientResponse) {
        // 缓存响应体,确保可多次订阅(日志+返回客户端)
        Mono<String> responseBodyMono = clientResponse.bodyToMono(String.class).cache();

        // 仅当状态码为400时记录响应
        Mono<Void> log400Mono = Mono.empty();
        if (clientResponse.statusCode().value() == 400) {
            log400Mono = responseBodyMono
                    .doOnNext(body -> log.error("Received HTTP 400 response. Headers: {}, Body: {}",
                            clientResponse.headers().asHttpHeaders(), body))
                    // 处理读取响应体时的异常,避免流中断
                    .onErrorResume(e -> {
                        log.error("Failed to read HTTP 400 response body", e);
                        return Mono.empty();
                    })
                    .then();
        }

        // 先执行日志记录,再构建返回给客户端的响应
        return log400Mono.then(Mono.defer(() ->
                ServerResponse.status(clientResponse.statusCode())
                        .headers(headerConsumer -> clientResponse.headers().asHttpHeaders().forEach(headerConsumer::addAll))
                        .body(responseBodyMono, String.class)
        ));
    }
}

关键实现细节

  • 响应体缓存:使用cache()操作符缓存响应体Mono,解决反应式流中响应体只能读取一次的问题,确保日志记录和返回客户端都能获取到响应内容。
  • 条件日志触发:通过判断clientResponse.statusCode().value() == 400精准触发日志逻辑,避免不必要的日志记录。
  • 反应式流程顺序:用log400Mono.then(...)保证日志记录逻辑先执行,再构建并返回响应,符合反应式编程的异步流特性。
  • 异常容错:添加onErrorResume处理读取响应体时的异常,防止日志逻辑的异常导致整个请求处理流中断。

内容的提问来源于stack exchange,提问作者pragnesh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 00:33:15