记录一次返回调用第三方API返回结果为乱码,出参无法解析为JSON的解决过程
个人项目需要实现返回指定城市天气信息的功能,因为对于数据没有什么需求,完全自娱自乐的项目,所以在网上找了一个免费的天气API。调用方式:feign
简单用浏览器请求了一下,返回结果如下:
第一次写法如下:
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 获取天气数据
*
* @author ZhangBoyuan
* @date 2022-06-14
*/
@FeignClient(name = "${thirdparty.weather.service-name}", url = "${thirdparty.weather.url}")
public interface WeatherService {
/**
* 获取指定城市天气
*/
@GetMapping(value = "/weather_mini")
JSONObject getCityWeather(@RequestParam("city") String cityName);
}
通过Collection层调用后,报错如下
系统提示存在非法字符,因此不可被解析。于是查看接口的响应头,发现接口传输的数据为压缩后格式
于是先将Feign中的出参格式改为更通用的String类型,先完成接收,然后一步步往下来,只要能拿到数据那一切好说
改进后的Feign Server如下
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 获取天气数据
*
* @author ZhangBoyuan
* @date 2022-06-14
*/
@FeignClient(name = "${thirdparty.weather.service-name}", url = "${thirdparty.weather.url}")
public interface WeatherService {
/**
* 获取指定城市天气
*/
@GetMapping(value = "/weather_mini")
String getCityWeather(@RequestParam("city") String cityName);
}
运行后参数正常可以被接收,打了个断点,查看出参
可以看到,出参是被压缩后的字符串,所以我们用JSON格式接收才会失败。这也意味着我们需要先将字符串进行解压,然后再解析成JSON格式,拿取需要的数据。考虑到这种需求可能会在项目的其他地方用到,因此将其编写为工具类,代码如下:
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
import java.util.zip.GZIPInputStream;
/**
* 字符串解压工具类
*
* @author ZhangBoyuan
* @date 2022-06-18
*/
public class StringUnzipUtil {
/**
* 解压gzip格式字符串
*/
public static String gzip(String str) {
try {
byte[] byteArray = str.getBytes(StandardCharsets.ISO_8859_1);
InputStream inputStream = new ByteArrayInputStream(byteArray);
GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(gzipInputStream));
String result = bufferedReader
.lines()
.collect(Collectors.joining());
return result;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
这里一定要注意这一行
byte[] test = str.getBytes(StandardCharsets.ISO_8859_1);
一定要按照IOS_8859_1的编码格式进行转换,否则会抛出如下异常,至于为什么会这样,因为涉及的内容比较多,后面如果有机会的话我会单独写一篇博客来说明。
java.util.zip.ZipException: Not in GZIP format
使用我们的工具类转换后,打断点,观察解压后数据,发现正是我们想用的。下面就更简单了,我们只要将其转换为JSON,然后再加一点点细节,最后return出去即可
完整代码如下:
Feign Server
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 获取天气数据
*
* @author ZhangBoyuan
* @date 2022-06-14
*/
@FeignClient(name = "${thirdparty.weather.service-name}", url = "${thirdparty.weather.url}")
public interface WeatherService {
/**
* 获取指定城市天气
*/
@GetMapping(value = "/weather_mini")
String getCityWeather(@RequestParam("city") String cityName);
}
Controller层
这里用了一部分项目中的类,无视掉即可,不影响主流程
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.var;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import pers.boyuan.common.constants.ResponseEnum;
import pers.boyuan.common.dto.Response;
import pers.boyuan.common.util.StringUnzipUtil;
import pers.boyuan.thirdparty.weather.WeatherService;
import java.util.Objects;
/**
* 天气相关 前端控制器
*
* @author ZhangBoyuan
* @date 2022-06-14
*/
@RestController
@RequestMapping("/thirdparty/weather")
@Api(tags = "天气相关接口")
public class WeatherController {
@Autowired
private WeatherService weatherService;
/**
* 天气api响应成功 code
*/
private final String RESPONSE_SUCCESS_CODE = "1000";
/**
* 天气api响应状态码 key
*/
private final String RESPONSE_STATUS_KEY = "status";
/**
* 天气api响应数据 key
*/
private final String RESPONSE_DATA_KEY = "data";
/**
* 天气api预测未来天气 key
*/
private final String RESPONSE_FORECAST_KEY = "forecast";
@GetMapping("/query")
@ApiOperation("根据城市名称查询天气")
public Response query(@RequestParam(name = "city") String cityName) {
String unzipStr = StringUnzipUtil.gzip(weatherService.getCityWeather(cityName));
if (Objects.nonNull(unzipStr)) {
JSONObject json = JSONObject.parseObject(unzipStr);
if (RESPONSE_SUCCESS_CODE.equals(json.getString(RESPONSE_STATUS_KEY))) {
return Response.success(json.getJSONObject(RESPONSE_DATA_KEY).getJSONArray(RESPONSE_FORECAST_KEY));
}
}
return Response.error(ResponseEnum.FAIL);
}
}
最后附上swagger中的执行结果