缺少使用 WebClient 发送 POST 请求的 Content-Length 标头(SpringBoot 2.0.2.RELEASE)

Posted

技术标签:

【中文标题】缺少使用 WebClient 发送 POST 请求的 Content-Length 标头(SpringBoot 2.0.2.RELEASE)【英文标题】:Missing Content-Length header sending POST request with WebClient (SpringBoot 2.0.2.RELEASE) 【发布时间】:2018-11-02 17:02:27 【问题描述】:

我正在使用 WebClient (SpringBoot 2.0.2.RELEASE) 发送带有 SOAP 请求的 POST,但它缺少所需的“Content-Length”标头旧版 API。

是否可以将 WebClient 配置为包含“Content-Length”标头? 在 SpringBoot 2.0.1 中为 EncoderHttpMessageWriter 解析并引入了 Spring Framework Issue,但它似乎不适用于 JAXB。

我尝试使用BodyInserters

webClient.post().body(BodyInserters.fromObject(request)).exchange();

syncBody:

webClient.post().syncBody(request).exchange();

他们都没有为WebClient工作。但是,当使用RestTemplate 时,Content-Length 已设置并且 API 响应成功

【问题讨论】:

【参考方案1】:

我正在努力解决同样的问题,作为一个丑陋的解决方法,我手动序列化请求(在我的情况下为 JSON)并设置长度(Kotlin 代码):

open class PostRetrieverWith411ErrorFix(
    private val objectMapper: ObjectMapper
) 

protected fun <T : Any> post(webClient: WebClient, body: Any, responseClass: Class<T>): Mono<T> 
    val bodyJson = objectMapper.writeValueAsString(body)

    return webClient.post()
        .contentType(MediaType.APPLICATION_JSON_UTF8)
        .contentLength(bodyJson.toByteArray(Charset.forName("UTF-8")).size.toLong())
        .syncBody(bodyJson)
        .retrieve()
        .bodyToMono(responseClass)
    

【讨论】:

目前看来只能这样了。经过一番研究,似乎没有办法使用当前的 webflux 实现来添加这个标头。【参考方案2】:

WebClient 是一个流媒体客户端,在流结束之前设置内容长度有点困难。到那时,标题早已不复存在。如果您使用旧版,您可以重新使用您的单声道(Mono/Flux 可以重复使用,Java 流不能)并检查长度。

    public void post() 

    Mono<String> mono = Mono.just("HELLO WORLDZ");

    final String response = WebClient.create("http://httpbin.org")
            .post()
            .uri("/post")
            .header(HttpHeaders.CONTENT_LENGTH,
                    mono.map(s -> String.valueOf(s.getBytes(StandardCharsets.UTF_8).length)).block())
            .body(BodyInserters.fromPublisher(mono, String.class))
            .retrieve()
            .bodyToMono(String.class)
            .block();

    System.out.println(response);


我的一位同事(干得好,Max!)提出了更简洁的解决方案,我添加了一些包装代码以便对其进行测试:

    Mono<String> my = Mono.just("HELLO WORLDZZ")
            .flatMap(body -> WebClient.create("http://httpbin.org")
                    .post()
                    .uri("/post")
                    .header(HttpHeaders.CONTENT_LENGTH,
                            String.valueOf(body.getBytes(StandardCharsets.UTF_8).length))
                    .syncBody(body)
                    .retrieve()
                    .bodyToMono(String.class));

    System.out.println(my.block());

【讨论】:

这仅适用于纯字符串主体,不适用于 POJO 到 JSON 之类的映射对象,还是这样? 自 2018 年以来有一个回复可以回答您的问题... 当您使用 WebClient .syncBody(myPojo) 的自动映射功能将 Pojo 作为 syncBody 传递时,即使 brebDev 的答案也无法解决这种情况,因为在这种情况下,我不知道序列化有多少个字符会,或者我可以吗?还是显式映射是唯一的出路?很抱歉我的第一个问题表述得很草率。 简单解决方案:使用 2.2 或更高版本。问题已修复:github.com/spring-projects/spring-framework/issues/21085 in 2019【参考方案3】:

如果您像我们一样应用 Sven 的 colleague(Max) 解决方案,您也可以将其调整为适用于您的身体是自定义 obj 但您必须对其进行序列化一次的情况:

String req = objectMapper.writeValueAsString(requestObject)

并将其传递给

webClient.syncBody(req)

请记住,在 SpringBoot 2.0.3.RELEASE 中,如果您将字符串作为请求传递给 webClient,它将作为 ContentType 标头 MediaType.TEXT_PLAIN 放置,这会使我们与其他服务的集成失败。我们通过像这样专门设置内容类型标题来解决这个问题:

httpHeaders.setContentType(MediaType.APPLICATION_JSON);

【讨论】:

以上是关于缺少使用 WebClient 发送 POST 请求的 Content-Length 标头(SpringBoot 2.0.2.RELEASE)的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 WebClient 反应式 Web 客户端发送带有 zip 正文的 POST 请求

如何在 webclient 的 post 请求中发送 x-www-form-urlencoded? [复制]

如何向 Discord Webhooks C# 发送 POST 请求

WebClient maxConnection 池限制?

Spring WebClient PageImpl Content 缺少字段

POST请求中缺少授权标头[重复]