如何记录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); }); }
关键说明
- 模拟请求写入:使用
MockClientHttpRequest让BodyInserter把请求数据写入其中,从而捕获原本无法直接读取的请求体内容。 - 复用消息写入器:使用WebClient默认的
HttpMessageWriter,确保模拟写入的逻辑和实际请求完全一致,避免解析出错。 - 解析multipart内容:通过
MultipartHttpMessageReader拆分出每个multipart部分,区分普通字段和文件字段分别记录。 - 避免干扰实际请求:处理完日志后必须返回原始请求,保证业务流程不受日志逻辑影响。
内容的提问来源于stack exchange,提问作者tweetysat
相关产品推荐
相关产品推荐

