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))
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With