稀土掘金技术社区

Nest 实现天气预报查询服务

点击公众关注号,“技术干货”及时达!

今天我们实现了一个查询城市天气预报的服务。

使用和风天气的免费 api。

免费的接口一天可以请求1000次,自己的项目就足够用了:

Image

最多可以查询未来7天的天气预报。

首先,登录和风天气,

然后在用户中心绑定邮箱和手机号:

Image

之后进入控制台,点击创建项目:

Image

这里大家选择免费订阅(我其他项目用了,免费名额了),指定密钥的名字:

Image

然后就可以看到你的key了:

Image

如果我们要查询青岛未来7天的天气。

需要先查询青岛的id:

Image

location是城市名字的拼音,然后带上刚才的钥匙:

https://devapi.qweather.com/v7/weather/7d?location=101120606&key=aff40f07926348b9b06f3229d2b52e6a

返回了青岛市和各个区的信息。

有了城市id之后就可以查询天气了:

https://api.qweather.com/v7/weather/7d?location=101120201&key=187d6c3dd15f4d2d99e2a7e0ee08ba04
Image

这里返回了从 2024-5-1 到 2024-5-7 的天气。

具体字段的解释可以看文档:

Image

这样我们就实现了查询某个城市7天天气的功能。

还有问题,现在是先用城市的拼音查的id,再用id查的天气。

那直接让用户输入城市拼音么?

这样也不好,我们可以用拼音这个包:

它可以获得中文的拼音:

Image

这样,整个流程就串起来了。

当然,如果您让用户直接选择城市,然后查询城市的天气,这样就可以获得所有城市的信息了。

网上有很多这样的JSON数据的:

Image

有所有城市名称及其 ID。

理清思路了,我们来写下代码:

npm install -g @nestjs/cli

nest new city-weather

Image

安装拼音包及其类型:

npm install --save pinyin@alpha
npm install --save-dev @types/pinyin

然后我们加个接口测试下:

Image
@Get('pinyin')
pinyin(@Query('text') text: string) {
    return pinyin(text).join('')
}

把服务运行起来:

npm run start:dev
Image

访问下一个试用:

Image

可以看到,确实返回了拼音。

但我们不需要知道是几声。

修改参数:

Image

这样就好了:

Image

那么nest服务里怎么访问三方接口呢?

直接用 axios 什么?

可以,但是我们希望统一配置 axios,然后各个模块都用同一个 axios 实例。

所以用@nestjs/axios这个包:

npm install --save @nestjs/axios axios

在AppModule中引入下HttpModule:

Image

这里可以填入各种请求配置,比如baseURL等,其实就是new了一个axios实例:

Image

然后在已使用的位置注入httpService:

Image
@Inject(HttpService)
private httpService: HttpService;

@Get('weather/:city')
async weather(@Param('city') city: string) {
  const cityPinyin = pinyin(city, { style: 'normal'}).join('');

  const { data } = await firstValueFrom(
    this.httpService.get(`https://geoapi.qweather.com/v2/city/lookup?location=${cityPinyin}&key=187d6c3dd15f4d2d99e2a7e0ee08ba04`)
  )

  return data;
}

使用@Param取路径中的参数。

然后用拼音获取城市的拼音,然后调用和风天气的接口。

这里为啥用firstValueFrom的rxjs操作符呢?

因为 HttpModule 把 axios 的方法返回值封装成了 rxjs 的 Observerable。

好处是你可以用 rxjs 的操作符了。

坏处是转成promise还得加一层firstValueFrom。

它就是用来把 rxjs Observable 转成 Promise 的:

Image

测试下:

Image

没啥问题。

然后继续调用天气预报:

@Get('weather/:city')
async weather(@Param('city') city: string) {
    const cityPinyin = pinyin(city, { style: 'normal'}).join('');

    const { data } = await firstValueFrom(
      this.httpService.get(`https://geoapi.qweather.com/v2/city/lookup?location=${cityPinyin}&key=187d6c3dd15f4d2d99e2a7e0ee08ba04`)
    )

    const location = data?.['location']?.[0];

    if(!location) {
      throw new BadRequestException('没有对应的城市信息');
    }

    const { data: weatherData } = await firstValueFrom(
      this.httpService.get(`https://api.qweather.com/v7/weather/7d?location=${location.id}&key=187d6c3dd15f4d2d99e2a7e0ee08ba04`)
    )

    return weatherData;
}

如果没有查到位置,返回 400 错误。

否则用location.id 查询该城市天气预报。

Image
Image

这样,我们的城市天气预报服务就完成了。

案例代码 上传了小册仓库

总结

我们基于和风天气的api实现了天气预报查询服务。

主要用了这个拼音包来完成中文转拼音,然后用拼音去请求和风的api查询城市id。

接下来用城市 id 请求天气数据。

和风天气的api免费版一天可以调用1000次,足够用了。

Nest 里发送 http 请求,我们用的是 @nestjs/axios 包的 HttpModule 的。

它可以统一配置,然后注入 HttpService 到可用的地方,并且 httpService 方法的返回值消耗成了 rxjs 的 Observerable,可以直接使用 rxjs 的操作符。

比喻用fistValueFrom来把rxjs的Observable转为Promise。

这样,您就可以在您的应用中集成提醒功能了。

点击公众关注号,“技术干货”及时达!