如何通过ExchangeFilterFunction修改WebClient请求体并添加自定义键值对
解决WebClient拦截Form请求体并添加自定义键值对的问题
要实现拦截POST请求的Form体并添加自定义键值对,核心难点在于FormInserter是延迟插入的,无法直接获取内部的MultiValueMap。下面是具体的实现方案:
核心思路
- 识别目标请求:仅处理POST方法且请求体为
BodyInserters.FormInserter的请求; - 提取原Form数据:通过模拟
FormInserter的插入操作,将内部键值对导出到临时MultiValueMap; - 修改并替换请求体:检查目标键是否存在,不存在则添加,再用修改后的
MultiValueMap重建FormInserter替换原请求体。
代码实现
自定义ExchangeFilterFunction
import org.springframework.http.HttpMethod; import org.springframework.http.client.reactive.ClientHttpRequest; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import org.springframework.web.reactive.function.client.ExchangeFunction; import reactor.core.publisher.Mono; public class FormEnhanceFilter implements ExchangeFilterFunction { private final String targetKey; private final String targetValue; public FormEnhanceFilter(String targetKey, String targetValue) { this.targetKey = targetKey; this.targetValue = targetValue; } @Override public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) { // 仅处理POST类型的Form请求 if (HttpMethod.POST.equals(request.method()) && request.body() instanceof BodyInserters.FormInserter) { BodyInserters.FormInserter<String> originalInserter = (BodyInserters.FormInserter<String>) request.body(); // 提取原Form中的所有键值对 MultiValueMap<String, String> formData = extractFormData(originalInserter); // 检查并添加自定义键值对 if (!formData.containsKey(targetKey)) { formData.add(targetKey, targetValue); } // 构建新的请求体并替换原请求 ClientRequest modifiedRequest = ClientRequest.from(request) .body(BodyInserters.fromFormData(formData)) .build(); return next.exchange(modifiedRequest); } // 非目标请求直接放行 return next.exchange(request); } private MultiValueMap<String, String> extractFormData(BodyInserters.FormInserter<String> inserter) { MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); // 调用insert方法将Form数据写入临时Map,这是最可靠的提取方式(避免反射依赖) inserter.insert(null, formData, null, null).block(); return formData; } }
应用过滤器到WebClient
如果是自己创建WebClient:
WebClient webClient = WebClient.builder() .filter(new FormEnhanceFilter("custom_key", "your_custom_value")) .build();
如果是替换第三方已创建的WebClient:
// thirdPartyWebClient为第三方提供的实例 WebClient modifiedWebClient = thirdPartyWebClient.mutate() .filter(new FormEnhanceFilter("custom_key", "your_custom_value")) .build();
关键说明
- 为什么用
insert方法提取:FormInserter没有暴露获取内部数据的公开方法,通过模拟插入到临时MultiValueMap是兼容性最好的方案,避免反射带来的版本适配问题; - 只针对POST+Form请求:避免拦截其他类型的请求(如JSON体、GET请求),减少不必要的性能开销;
- 线程安全:
LinkedMultiValueMap是线程安全的,且操作都是在请求处理的单线程上下文内执行,不会有并发问题。
内容的提问来源于stack exchange,提问作者Ock
相关产品推荐
相关产品推荐

