Chinese garbled characters may occur when the Spring RestTemplate calls the weather forecast interface. The solution is as follows:

Problems arise

We found a free online weather forecast interface WTHRCDN. Etouch. Cn/weather_min… . We want to call this interface and parse the returned data into JSON format.

Core business logic is as follows:

private WeatherResponse doGetWeatherData(String uri) {

    ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
    
    String strBody = null;

    if (response.getStatusCodeValue() == 200) {
        strBody = response.getBody();
    }

    ObjectMapper mapper = new ObjectMapper();
    WeatherResponse weather = null;

    try {
        weather = mapper.readValue(strBody, WeatherResponse.class);
    } catch (IOException e) {
        e.printStackTrace();
    }

    return weather;
}Copy the code

The interface works fine in a browser. As shown below:

In a pure Spring application, however, an attempt to use the RestTemplate call fails to parse the data as JSON because the data is garbled. As shown below:

A code conversion is attempted

At first, we thought this might be caused by the data being forwarded by someone other than UTF-8, so we tried to add a message converter.

@Configuration public class RestConfiguration { @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // Support Chinese encoding return restTemplate; }}Copy the code

StringHttpMessageConverter ISO_8859_1 by default, so we set to UTF_8.

Execute again, find still is garbled.

Get to the root of the problem

Instead of guessing, I took a closer look at the HTTP request protocol. Find clues in the story:

It turns out that the data is GZIP compressed. By default, RestTemplate uses the JDK’s HTTP invocator and does not support GZIP decompression, so no wonder it fails to parse.

The solution

Now that we have found the problem, it is easy to solve it. The following schemes are mainly considered.

1. Write the GIZP utility class

The tool classes for handling Gizp compressed data are as follows:

/** * Welcome to https://waylau.com */ package com.waylau.spring.mvc.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPInputStream; @author <a href="https://waylau.com ">Way Lau</a> */ public class StringUtil {/** * processes Gizp compressed data. ** @param STR * @return * @throws IOException */ public static String conventFromGzip(String str) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayInputStream in; GZIPInputStream gunzip = null; in = new ByteArrayInputStream(str.getBytes("ISO-8859-1 ")); gunzip = new GZIPInputStream(in); byte[] buffer = new byte[256]; int n; while ((n = gunzip.read(buffer)) >= 0) { out.write(buffer, 0, n); } return out.toString(); }}Copy the code

Core business logic is as follows:

private WeatherResponse doGetWeatherData(String uri) {

    ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
    String strBody = null;

    if (response.getStatusCodeValue() == 200) {
        try {
            strBody = StringUtil.conventFromGzip(response.getBody());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    ObjectMapper mapper = new ObjectMapper();
    WeatherResponse weather = null;

    try {
        weather = mapper.readValue(strBody, WeatherResponse.class);
    } catch (IOException e) {
        e.printStackTrace();
    }

    return weather;
}Copy the code

2. Use Apache HttpClient

Use Apache HttpClient as the REST client. Apache HttpClient has built-in support for GZIP

@Configuration public class RestConfiguration { @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate( new HttpComponentsClientHttpRequestFactory()); // Using HttpClient, Support GZIP restTemplate. GetMessageConverters (). The set (1, new StringHttpMessageConverter (StandardCharsets. UTF_8)); // Support Chinese encoding return restTemplate; }}Copy the code

Core business logic is as follows:

private WeatherResponse doGetWeatherData(String uri) {

    ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
    
    String strBody = null;

    if (response.getStatusCodeValue() == 200) {
        strBody = response.getBody();
    }

    ObjectMapper mapper = new ObjectMapper();
    WeatherResponse weather = null;

    try {
        weather = mapper.readValue(strBody, WeatherResponse.class);
    } catch (IOException e) {
        e.printStackTrace();
    }

    return weather;
}Copy the code

Of course, using this scenario requires the introduction of Apache HttpClient dependencies.

The end result, perfect!

Differences used in Spring Boot

Some students also asked why I did not use RestTemplate to call the same interface in the course “Micro-service Practice based on Spring Cloud”, why there was no garbled code problem?

In fact, careful students should notice that in the course, we also use Apache HttpClient, since the Spring Cloud itself is based on Spring Boot, so the details of the message transformation is shielded.

Here is how to build a RestTemplate from RestTemplateBuilder in Spring Boot:

@Configuration public class RestConfiguration { @Autowired private RestTemplateBuilder builder; @Bean public RestTemplate restTemplate() { return builder.build(); }}Copy the code

So learn to code, know why to know why!

The source code

  • See “Spring 5 Examples” for the source code of this article (github.com/waylau/spri…). “Resttemplate-based weather forecasting service” example

Reference:

  • Spring Boot Tutorial :github.com/waylau/spri…
  • The blog system based on Spring Boot of actual combat: coding.imooc.com/class/125.h…
  • waylau.com/spring-rest…