记录一次返回调用第三方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中的执行结果

posted @ 2022-06-21 20:58  张知非  阅读(657)  评论(0编辑  收藏  举报