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

如何记录Spring 6 WebClient的multipart/form-data请求体内容

Spring Boot 3 WebClient 记录 Multipart/Form-Data 请求体完整内容

核心问题

BodyInserter 是用于向请求写入数据的对象,无法直接读取内容,需要通过模拟请求写入过程捕获数据后,再解析 multipart 的各个部分。

解决方案

修改你的 logRequest 方法,通过模拟请求写入、解析 multipart 内容来实现完整日志记录:

private ExchangeFilterFunction logRequest() {
    return ExchangeFilterFunction.ofRequestProcessor(request -> {
        log.debug("--- Request -------------------------------------------------------------------------");
        // 打印请求头信息
        request.headers().forEach((name, values) -> values.forEach(value -> log.debug("{} -> {}", name, value)));
        log.debug("[ {} / {} ]", request.method(), request.url());

        // 仅处理multipart/form-data类型的请求
        if (MediaType.MULTIPART_FORM_DATA.includes(request.headers().getContentType())) {
            BodyInserter<?, ? super ClientHttpRequest> bodyInserter = request.body();
            MockClientHttpRequest mockRequest = new MockClientHttpRequest(request.method(), request.url());
            // 复制原始请求头到模拟请求,保证解析逻辑正确
            mockRequest.getHeaders().putAll(request.headers());

            // 让BodyInserter将数据写入模拟请求
            return bodyInserter.insert(mockRequest, new BodyInserter.Context() {
                @Override
                public List<HttpMessageWriter<?>> messageWriters() {
                    // 复用WebClient默认的消息写入器,确保和实际请求逻辑一致
                    return WebClient.builder().build().getExchangeStrategies().messageWriters();
                }

                @Override
                public List<HttpMessageConverter<?>> messageConverters() {
                    return Collections.emptyList();
                }

                @Override
                public Optional<ServerHttpRequest> serverRequest() {
                    return Optional.empty();
                }
            }).then(Mono.defer(() -> {
                // 解析multipart请求体
                MultipartHttpMessageReader multipartReader = new MultipartHttpMessageReader();
                return multipartReader.readMono(Multipart.class, mockRequest, Collections.emptyMap())
                        .doOnNext(multipart -> {
                            log.debug("--- Request Body (Multipart Parts) ---------------------------------------------------");
                            // 遍历每个multipart部分,记录详细信息
                            multipart.getParts().forEach(part -> {
                                log.debug("Part Name: {}", part.name());
                                log.debug("Content Type: {}", part.headers().getContentType());
                                
                                // 区分普通表单字段和文件字段
                                if (part instanceof FormFieldPart formField) {
                                    log.debug("Field Value: {}", formField.value());
                                } else if (part instanceof FilePart filePart) {
                                    log.debug("File Name: {}", filePart.filename());
                                    // 可选:记录文件大小
                                    filePart.content()
                                            .map(DataBuffer::readableByteCount)
                                            .reduce(Long::sum)
                                            .doOnNext(size -> log.debug("File Size: {} bytes", size))
                                            .subscribe();
                                }
                                log.debug("-------------------------------------------------------------------------------------");
                            });
                        })
                        .thenReturn(request); // 返回原始请求,不影响实际发送流程
            });
        }

        log.debug("-------------------------------------------------------------------------------------");
        return Mono.just(request);
    });
}

关键说明

  1. 模拟请求写入:使用 MockClientHttpRequest 让 BodyInserter 把请求数据写入其中,从而捕获原本无法直接读取的请求体内容。
  2. 复用消息写入器:使用WebClient默认的 HttpMessageWriter,确保模拟写入的逻辑和实际请求完全一致,避免解析出错。
  3. 解析multipart内容:通过 MultipartHttpMessageReader 拆分出每个multipart部分,区分普通字段和文件字段分别记录。
  4. 避免干扰实际请求:处理完日志后必须返回原始请求,保证业务流程不受日志逻辑影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:40:29