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

六月ゝ 毕业季﹏ 提交于 2020-07-19 06:11:53

问题


I'm using WebClient (SpringBoot 2.0.2.RELEASE) to send a POST with SOAP request, but it is missing "Content-Length" header required by the legacy API.

Is it possible to configure WebClient to include "Content-Length" header? There is an Spring Framework Issue resolved and introduced for EncoderHttpMessageWriter in SpringBoot 2.0.1, but it seems not to work for JAXB.

I tried to use BodyInserters:

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

and syncBody:

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

None of them worked for WebClient. Though, when RestTemplate is used, Content-Length is set and API responds with success


回答1:


WebClient is a streaming client and it's kind of difficult to set the content length until the stream has finished. By then the headers are long gone. If you work with legacy, you can re-use your mono (Mono/Flux can be reused, Java streams not) and check the length.

    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);

}

A colleague (well done Max!) of mine came up with cleaner solution, I added some wrapping code so it can be tested:

    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());



回答2:


I am struggling with the same problem, as an ugly work-around I am manually serializing the request (JSON in my case) and setting the length (Kotlin code):

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)
    }
}



回答3:


If you apply Sven's colleague(Max) solution like we did you can also adapt it for cases like your body being a custom obj but you have to serialize it once:

String req = objectMapper.writeValueAsString(requestObject)

and passed that to

webClient.syncBody(req)

Keep in mind that with SpringBoot 2.0.3.RELEASE, if you'll pass a String to webClient as a request, it will put as ContentType header MediaType.TEXT_PLAIN and that made our integration with other service to fail. We fixed that by setting specifically content type header like this:

httpHeaders.setContentType(MediaType.APPLICATION_JSON);


来源:https://stackoverflow.com/questions/50492890/missing-content-length-header-sending-post-request-with-webclient-springboot-2

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!