Декодируйте контент-кодировку gzip с помощью Spring WebClient

Я звоню в веб-сервис, используя Spring WebClient (Spring 5.1.3). Служба отвечает content-type: application/json а также content-encoding: gzip

ClientResponse.bodyToMono затем происходит сбой с ошибкой "Ошибка декодирования JSON: недопустимый символ ((CTRL-CHAR, код 31))", которая, как я полагаю, связана с тем, что содержимое не было декодировано перед попыткой анализа JSON.

Вот фрагмент кода (упрощенно) о том, как я создаю WebClient

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

Затем я использую WebClient, чтобы сделать вызов:

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

HTTP-запрос имеет 2 заголовка:

Accept: application/json
Accept-Encoding: gzip

Ответ имеет следующие заголовки:

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

Делая следующее, я могу вручную декодировать содержимое GZIP и получить действительный JSON из результата

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 ответ

Решение

Эта функция изначально поддерживается клиентом реактора netty.

Вы должны создать HttpClient как это:

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

И тогда нет необходимости добавлять заголовок запроса на принятие кодировки, так как это сделано для вас.

Обратите внимание, что этот бит выполняется самим соединителем, если вы не HttpClient пример.

Другие вопросы по тегам