6

I need to do a POST using WebClient and the server requires the body to be compressed. I've checked previous questions asked here and here, but none helped me understand what needs to be done.

My code looks something like this:

webClient.post()
    .bodyValue(requestBody)
    .retrieve()
    .bodyToMono(Response.class)

I'd like to send the requestBody compressed using gzip. We were doing it with RestTemplate and a custom GZipFilter but I cannot see how to do it now with the WebClient.

Rubasace
  • 939
  • 8
  • 18

1 Answers1

6

I have implemented sample code to help you with this. You will need to clean this up and adapt to your needs, but I have tested this and it does work.

The first step is to implement an Encoder<T> where <T> is the type of the object you want to encode. In my example I am using JsonNode.

public class GzipEncoder extends AbstractEncoder<JsonNode> {

    public GzipEncoder() {
        super(MediaType.APPLICATION_JSON);
    }

    @Override
    public boolean canEncode(ResolvableType elementType, MimeType mimeType) {
        return MediaType.APPLICATION_JSON.equalsTypeAndSubtype(mimeType) && elementType.isAssignableFrom(JsonNode.class);
    }

    @Override
    public Flux<DataBuffer> encode(Publisher<? extends JsonNode> inputStream, DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType, Map<String, Object> hints) {
        return Flux.from(inputStream).map((JsonNode node) ->
                encodeValue(node, bufferFactory, elementType, mimeType, hints));
    }

    @Override
    public DataBuffer encodeValue(JsonNode node, DataBufferFactory bufferFactory, ResolvableType valueType, MimeType mimeType, Map<String, Object> hints) {
        return bufferFactory.wrap(gzip(node.toString()));
    }

    private byte[] gzip(String value) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(baos)) {
            gzipOutputStream.write(value.getBytes());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return baos.toByteArray();
    }
}

Then you also have to implement an HttpMessageWriter

public class GzipHttpMessageWriter extends EncoderHttpMessageWriter {

    public GzipHttpMessageWriter() {
        super(new GzipEncoder());
    }

    @Override
    public Mono<Void> write(Publisher inputStream, ResolvableType elementType, MediaType mediaType, ReactiveHttpOutputMessage message, Map hints) {
        return super.write(inputStream, elementType, mediaType, updateContentEncoding(message), hints);
    }

    private ReactiveHttpOutputMessage updateContentEncoding(ReactiveHttpOutputMessage message) {
        message.getHeaders().add("Content-Encoding", "gzip");
        return message;
    }
}

Now create your WebClient as follows (I have added wiretap to confirm gzip is working)

WebClient webclientGzip = WebClient.builder()
        .codecs(clientCodecConfigurer -> clientCodecConfigurer.customCodecs().register(new GzipHttpMessageWriter()))
        .clientConnector(new ReactorClientHttpConnector(HttpClient.create().wiretap(true)))
        .build();

Now when I post a JsonNode body with the following, I can see the request going out encoded with gzip

webclientGzip.post().uri(uri)
            .accept(MediaType.APPLICATION_JSON)
            .contentType(MediaType.APPLICATION_JSON)
            .body(Mono.just(body), JsonNode.class)
athom
  • 1,428
  • 4
  • 13
  • 26