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

SpringBoot 2.0.2中WebClient发送SOAP POST请求缺失Content-Length头

解决WebClient发送SOAP请求时Content-Length头缺失的问题

我完全理解你碰到的麻烦——在Spring Boot 2.0.2.RELEASE里用WebClient发送SOAP POST请求时,遗留API要求的Content-Length头总是不见,试过BodyInserters.fromObject和syncBody两种方式都没用,但换成RestTemplate就能正常设置这个头,API也能顺利响应。下面给你几个实用的解决方案:

方案1:手动序列化请求并设置Content-Length

既然WebClient默认不会帮JAXB序列化的请求计算Content-Length,我们可以自己先把请求对象转成字节数组,算好长度后再手动加到请求头里:

// 1. 初始化JAXB序列化工具
JAXBContext jaxbContext = JAXBContext.newInstance(YourSoapRequestClass.class);
Marshaller marshaller = jaxbContext.createMarshaller();

// 2. 将请求对象序列化为字节数组
ByteArrayOutputStream baos = new ByteArrayOutputStream();
marshaller.marshal(yourSoapRequestObject, baos);
byte[] requestBytes = baos.toByteArray();

// 3. 构建WebClient请求,手动指定Content-Length
webClient.post()
    .uri("your-legacy-api-endpoint")
    .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(requestBytes.length))
    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
    .body(BodyInserters.fromByteArray(requestBytes))
    .exchange();

这种方式直接跳过WebClient默认的序列化逻辑,完全由我们掌控请求体的长度计算,能确保Content-Length头被正确设置。

方案2:自定义HttpMessageWriter自动计算Content-Length

如果你不想每次请求都手动序列化,可以通过自定义EncoderHttpMessageWriter来增强JAXB的处理逻辑,让它自动计算并添加Content-Length头:

@Bean
public WebClient webClient() {
    ExchangeStrategies strategies = ExchangeStrategies.builder()
            .messageWriters(writers -> {
                // 遍历默认消息写入器,找到JAXB对应的那个
                for (HttpMessageWriter<?> writer : writers) {
                    if (writer instanceof EncoderHttpMessageWriter) {
                        Encoder<?> encoder = ((EncoderHttpMessageWriter<?>) writer).getEncoder();
                        if (encoder instanceof Jaxb2XmlEncoder) {
                            // 替换为自定义的写入器
                            EncoderHttpMessageWriter<?> customWriter = new EncoderHttpMessageWriter<>(
                                    encoder, MediaType.APPLICATION_XML, MediaType.TEXT_XML) {
                                @Override
                                public Mono<Void> write(Publisher<?> inputStream, ResolvableType elementType, 
                                                       MediaType mediaType, ReactiveHttpOutputMessage message, 
                                                       Map<String, Object> hints) {
                                    // 先收集请求对象并序列化,计算长度后设置请求头
                                    return Mono.from(inputStream)
                                            .map(obj -> {
                                                try {
                                                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                                                    JAXBContext context = JAXBContext.newInstance(obj.getClass());
                                                    Marshaller marshaller = context.createMarshaller();
                                                    marshaller.marshal(obj, baos);
                                                    return baos.toByteArray();
                                                } catch (JAXBException e) {
                                                    throw new RuntimeException("JAXB序列化失败", e);
                                                }
                                            })
                                            .doOnNext(bytes -> message.getHeaders().setContentLength(bytes.length))
                                            .flatMap(bytes -> super.write(Mono.just(bytes), 
                                                                        ResolvableType.forClass(byte[].class), 
                                                                        mediaType, message, hints));
                                }
                            };
                            writers.set(writers.indexOf(writer), customWriter);
                            break;
                        }
                    }
                }
            })
            .build();

    return WebClient.builder()
            .exchangeStrategies(strategies)
            .build();
}

配置完成后,后续用WebClient发送JAXB序列化的SOAP请求时,就会自动计算并添加Content-Length头了。

为什么RestTemplate可以正常工作?

RestTemplate是同步阻塞式的客户端,它会在发送请求前就把对象完整序列化成字节数组,然后直接根据数组长度设置Content-Length头。而WebClient是响应式非阻塞的,默认会采用分块传输(Transfer-Encoding: chunked),这种模式下不需要提前知道内容长度,所以不会自动添加Content-Length——这也是你遇到问题的核心原因。

至于SPR-16542的修复,它确实优化了EncoderHttpMessageWriter的逻辑,但Jaxb2XmlEncoder因为序列化的特性,没办法提前预估内容长度,所以这个修复并没有覆盖到JAXB的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:51:45