Decode content-encoding gzip using Spring WebClient

Viewed 7490

I am calling a web service using Spring WebClient (Spring 5.1.3). The service responds with content-type: application/json and content-encoding: gzip

ClientResponse.bodyToMono then fails with the error "JSON decoding error: Illegal character ((CTRL-CHAR, code 31))" which I assume is because the content has not been decoded before trying to parse the JSON.

Here is code snippet (simplified) of how I create the WebClient

HttpClient httpClient = HttpClient.create().secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)).build();

I then use the WebClient to make the call:

webClient.get().uri(uri)
    .accept(MediaType.APPLICATION_JSON)
    .header(HttpHeaders.ACCEPT_ENCODING, "gzip")
    .exchange()

The HTTP request has 2 headers:

Accept: application/json
Accept-Encoding: gzip

The response has the following headers:

set-cookie: xxx
content-type: application/json; charset=utf-8
content-length: 1175
content-encoding: gzip
cache-control: no-store, no-cache

By doing the following I am able to manually decode the GZIP content and get valid JSON from the result

webClient.get().uri(uri)
        .accept(MediaType.APPLICATION_JSON)
        .header("accept-encoding", "gzip")
        .exchange()
        .flatMap(encodedResponse -> encodedResponse.body((inputMessage, context) ->
                inputMessage.getBody().flatMap(dataBuffer -> {
                    ClientResponse.Builder decodedResponse = ClientResponse.from(encodedResponse);
                    try {
                        GZIPInputStream gz = new GZIPInputStream(dataBuffer.asInputStream());
                        decodedResponse.body(new String(gz.readAllBytes()));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    decodedResponse.headers(headers -> {
                        headers.remove("content-encoding");
                    });
                    return Mono.just(decodedResponse.build());
                }).flatMap(clientResponse -> clientResponse.bodyToMono(Map.class))
1 Answers

This feature is supported natively by the reactor netty client.

You should create HttpClient like this:

HttpClient httpClient = HttpClient.create()
             .secure(sslContextSpec -> sslContextSpec.sslContext(sslContext))
             .compress(true);

And then there's no need to add the accept encoding request header since it's done for you.

Note that this bit is done by the connector itself when you don't provide a custom HttpClient instance.

Related