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

时间:2018-05-23 16:03:33

标签: spring-boot spring-webflux

我使用WebClient(SpringBoot 2.0.2.RELEASE)发送带有 SOAP 请求的POST,但它缺少" 内容长度"遗留API所需的标头。

是否可以将WebClient配置为包含" 内容长度"头? 在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会成功响应

3 个答案:

答案 0 :(得分:2)

WebClient是流客户端,在流完成之前很难设置内容长度。届时,标题将不复存在。如果您使用的是旧版,则可以重用mono(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());

答案 1 :(得分: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)
    }
}

答案 2 :(得分:0)

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

String req = objectMapper.writeValueAsString(requestObject)

并将其传递给

webClient.syncBody(req)

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

httpHeaders.setContentType(MediaType.APPLICATION_JSON);