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

Missing Content-Length header sending POST request with WebClient (SpringBoot 2.0.2.RELEASE)

我正在使用 WebClient (SpringBoot 2.0.2.RELEASE) 发送带有 SOAP 请求的 POST,但它丢失了“Content-Length" header 遗留 API 所要求的。

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

我尝试使用 BodyInserters:

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

syncBody:

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

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

我正在努力解决同样的问题,作为一个丑陋的解决方法,我正在手动序列化请求(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)
    }
}

WebClient 是一个流媒体客户端,在流结束之前设置内容长度有点困难。到那时 headers 早已不复存在。如果你使用旧版,你可以 re-use 你的单声道(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());

如果您像我们一样应用 Sven 的同事 (Max) 解决方案,您也可以将其调整为适用于 body 作为自定义对象但必须序列化一次的情况:

String req = objectMapper.writeValueAsString(requestObject)

并将其传递给

webClient.syncBody(req)

请记住,使用 SpringBoot 2.0.3.RELEASE,如果您将字符串作为请求传递给 webClient,它将作为 ContentType header MediaType.TEXT_PLAIN 进行处理我们与其他服务的整合失败。我们通过专门设置内容类型 header 来解决这个问题:

httpHeaders.setContentType(MediaType.APPLICATION_JSON);