J'utilise WebClient (SpringBoot 2.0.2.RELEASE) pour envoyer un POST avec une requête SOAP , mais il manque l'en-tête " Content-Length " requis par l'ancienne API .

Est-il possible de configurer WebClient pour inclure l'en-tête " Content-Length "? Un Spring Framework Issue a été résolu et introduit pour EncoderHttpMessageWriter dans SpringBoot 2.0.1, mais cela ne semble pas fonctionner pour JAXB.

J'ai essayé d'utiliser BodyInserters:

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

Et syncBody:

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

Aucun d'entre eux n'a fonctionné pour WebClient. Cependant, lorsque RestTemplate est utilisé, Content-Length est défini et l'API répond avec succès

2
Wojciech Marusarz 23 mai 2018 à 19:03

3 réponses

Meilleure réponse

WebClient est un client de streaming et il est assez difficile de définir la longueur du contenu tant que le flux n'est pas terminé. D'ici là, les en-têtes ont disparu depuis longtemps. Si vous travaillez avec l'héritage, vous pouvez réutiliser votre mono (Mono / Flux peut être réutilisé, les flux Java non) et vérifier la longueur.

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

}

Un de mes collègues (bravo Max!) A proposé une solution plus propre, j'ai ajouté du code d'emballage pour qu'il puisse être testé:

    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
Sven 26 juin 2018 à 20:52

Je suis aux prises avec le même problème, car je suis en train de sérialiser manuellement la demande (JSON dans mon cas) et de définir la longueur (code 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)
    }
}
1
Milosz Tylenda 24 mai 2018 à 08:06

Si vous appliquez la solution du collègue (Max) de Sven comme nous l'avons fait, vous pouvez également l'adapter pour des cas tels que votre corps étant un objet personnalisé, mais vous devez le sérialiser une fois:

String req = objectMapper.writeValueAsString(requestObject)

Et passé cela à

webClient.syncBody(req)

Gardez à l'esprit qu'avec SpringBoot 2.0.3.RELEASE, si vous transmettez une chaîne à webClient en tant que demande, elle sera placée comme en-tête ContentType MediaType.TEXT_PLAIN et cela a fait échouer notre intégration avec un autre service. Nous avons corrigé cela en définissant spécifiquement l'en-tête de type de contenu comme ceci:

httpHeaders.setContentType(MediaType.APPLICATION_JSON);
0
brebDev 5 sept. 2018 à 15:26