Как сжать JSON тело запроса с WebClient? - PullRequest
2 голосов
/ 10 марта 2020

Мне нужно сделать POST, используя WebClient, а серверу требуется сжать тело. Я проверил предыдущие вопросы здесь и здесь , но ни один из них не помог мне понять, что нужно сделать.

Мой код выглядит примерно так:

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

Я бы хотел отправить requestBody в сжатом виде с помощью gzip. Мы делали это с RestTemplate и пользовательским GZipFilter, но я не вижу, как это сделать сейчас с WebClient.

1 Ответ

1 голос
/ 17 марта 2020

Я реализовал пример кода, чтобы помочь вам в этом. Вам нужно будет это исправить и адаптировать к вашим потребностям, но я проверил это, и оно работает.

Первый шаг - реализовать Encoder<T>, где <T> - это тип объекта, который вы используете. хочу кодировать. В моем примере я использую 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();
    }
}

Затем вы также должны реализовать 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;
    }
}

Теперь создайте свой WebClient следующим образом (я добавил прослушку подтвердите, что gzip работает)

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

Теперь, когда я публикую тело JsonNode со следующим сообщением, я вижу, как выходит запрос, закодированный с помощью gzip

webclientGzip.post().uri(uri)
            .accept(MediaType.APPLICATION_JSON)
            .contentType(MediaType.APPLICATION_JSON)
            .body(Mono.just(body), JsonNode.class)
...