Spring MVC 如何在Spring-Web中使用RestTemplate解析gzip编码的响应

6bc51xsx  于 2022-11-14  发布在  Spring
关注(0)|答案(3)|浏览(185)

在我修改Consuming a RESTful Web Service示例以从www.example.com调用get users by id之后api.stackexchange.com,我得到JsonParseException:
com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens
来自api.stackexchange.com的响应是gzip压缩的。

如何将对gzip压缩响应的支持添加到Spring-Web RestTemplate中?

我正在使用Sping Boot 父版本1.3.1.RELEASE,因此使用Spring-Web 4.2.4-RELEASE
下面是我调整后的例子:
User.java

package stackexchange.dto;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonNaming(LowerCaseWithUnderscoresStrategy.class)
public class User {

    // Properties made public in order to shorten the example
    public int userId;
    public String displayName;
    public int reputation;

    @Override
    public String toString() {
        return "user{"
                + "display_name='" + displayName + '\''
                + "reputation='" + reputation + '\''
                + "user_id='" + userId + '\''
                + '}';
    }
}

CommonWrapper.java

package stackexchange.dto;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonNaming(LowerCaseWithUnderscoresStrategy.class)
public class CommonWrapper {

    // Properties made public in order to shorten the example
    public boolean hasMore;
    // an array of the type found in type
    public User[] items;
    public int page;
    public int pageSize;
    public int quotaMax;
    public int quotaRemaining;

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (User user : items) {
            sb.append("{" + user.toString() + "}\n");
        }

        return "common_wrapper{"
        + "\"items\"=[\n"
        + sb
        + "]"
        + "has_more='" + hasMore + '\''
        + "page='" + page + '\''
        + "page_size='" + pageSize + '\''
        + "quota_max='" + quotaMax + '\''
        + "quota_remaining='" + quotaRemaining + '\''
        + '}';
    }
}

StackExchange.java

package stackexchange;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.web.client.RestTemplate;

import stackexchange.dto.CommonWrapper;

import com.fasterxml.jackson.databind.PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

@JsonNaming(LowerCaseWithUnderscoresStrategy.class)
public class StackExchange implements CommandLineRunner{

    private static final Logger log = LoggerFactory.getLogger(StackExchange.class);

    public static void main(String args[]) {
        SpringApplication.run(StackExchange.class);
    }

    @Override
    public void run(String... strings) throws Exception {

        RestTemplate restTemplate = new RestTemplate();
        CommonWrapper response = restTemplate
                .getForObject(
                        "https://api.stackexchange.com/2.2/users/4607349?site=stackoverflow",
                        CommonWrapper.class);

        log.info(response.toString());
    }

}

pom.xml -与示例中的相同

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>stackexchangetest</groupId>
  <artifactId>stackexchangetest</artifactId>
  <version>0.0.1</version>
  <name>stackexchangetest</name>
  <description>api.stackexchange.com Test</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.1.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>   
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
thtygnil

thtygnil1#

将默认的requestFactory替换为Apache HttpClient中的requestFactory(它动态解码GZIP):

HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(
            HttpClientBuilder.create().build());
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);

将Apache Http客户端添加到pom.xml中

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <!--Version is not needed when used with Spring Boot parent pom file -->
    <version>4.5.1</version>
</dependency>
f45qwnt8

f45qwnt82#

private String callViaRest(String requestString, Steps step) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.TEXT_XML);
    headers.add("Accept-Encoding", "application/gzip");
    HttpEntity<String> entity = new HttpEntity<String>(requestString, headers);

    byte[] responseBytes = jsonRestTemplate
            .exchange("yourUrl", HttpMethod.POST, entity, byte[].class).getBody();
    String decompressed = null;
    try {
        decompressed= new String(CompressionUtil.decompressGzipByteArray(responseBytes),Charsets.UTF_8);
    } catch (IOException e) {
        LOGGER.error("network call failed.", e);
    }
    return decompressed;
}
iqjalb3h

iqjalb3h3#

我只想评论wxh对公认答案的评论。评论太短,很难阅读其中的代码。wxh说公认的答案是“不完美”的解决方案,但...嗯,它至少是“相当完美”的,对吗?如果你想进入wxh提到的细节,你可以随时使用,我总是这样做。(虽然我避免使用resttemplate,但这只是我的口味)一个自定义的httpclient,并根据您的口味设置它。在这个config setting up compression support for a custom httpclient中,它将支持所有三种最常见的压缩方法,gzip,deflate和brotli。如果您想控制超时等,只需在

private static final RequestConfig DEFAULT_CONFIG = RequestConfig.custom()
        .setConnectTimeout(20 * 1000)           // the time to establish the connection with the remote host
        .setConnectionRequestTimeout(10 * 1000) // timeout used when requesting a connection from the local cm
        .setSocketTimeout(40 * 1000).build();   // the time waiting for data – after establishing the connection; 
                                                // maximum time of inactivity between two data packets

或者像这样的东西,然后把它挂在

.setDefaultRequestConfig(DEFAULT_CONFIG)
..
.build();

和/或如果您还(如wxh所建议的)想要控制连接池等,则添加如下内容

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        // Increase max total connection to 200
        cm.setMaxTotal(200);
        // Increase default max connection per route to 20
        cm.setDefaultMaxPerRoute(20);

        SocketConfig sc = SocketConfig.custom().setSoKeepAlive(true)
                .setSoReuseAddress(true)
                .setTcpNoDelay(true)
                .setSoTimeout(40 * 1000)
                .setSoLinger(5).build();
        cm.setDefaultSocketConfig(sc);

(200对于最大并行请求可能会有一点开销,以上是针对特殊情况的..)如果这是您想要的,则将其挂接为

.setConnectionManager(cm)
   ..
   .build();

或者简而言之--使用自定义的httpclient,你几乎可以控制每一个细节。

.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))

但是,当你完成并感到高兴时,就按照公认答案中的描述将其挂起

HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(myCustomHttpClient);
    RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);

相关问题