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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot项目中新增一个第三方API并以JSON格式返回给前端可以按照以下步骤进行: 1. 导入相关依赖:在项目的pom.xml文件中添加对相关依赖的引用,例如使用RestTemplate来发送HTTP请求,可以添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` 2. 创建一个Controller:在项目中创建一个Controller类,用于处理前端的请求和返回JSON数据。可以使用`@RestController`注解标记该类,并使用`@RequestMapping`注解指定API的URL路径。 3. 调用第三方API:在Controller中使用`RestTemplate`发送HTTP请求调用第三方API,并获取返回的数据。可以使用`getForObject()`方法发送GET请求,或者使用`postForObject()`方法发送POST请求。 4. 封装返回结果:将从第三方API获取到的数据封装到一个自定义的Java对象中,并使用`@ResponseBody`注解将该对象转换为JSON格式返回给前端。可以使用`ResponseEntity`类来包装返回结果,设置相应的HTTP状态码和响应头信息。 以下是一个示例代码: ```java import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/api") public class ThirdPartyApiController { @GetMapping("/data") public ResponseEntity<Object> getDataFromThirdPartyApi() { RestTemplate restTemplate = new RestTemplate(); String apiUrl = "http://third-party-api.com/data"; // 第三方API的URL // 发送HTTP请求调用第三方API Object response = restTemplate.getForObject(apiUrl, Object.class); // 封装返回结果并以JSON格式返回给前端 return ResponseEntity.ok(response); } } ``` 在上面的示例中,当访问`/api/data`路径时,将会调用第三方API并将其返回的数据以JSON格式返回给前端。 请注意,上述代码仅为示例,实际应用中可能还需要进行异常处理、参数校验等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值