点击上方蓝字关注“汪宇杰博客”
导语
最近因为疫情被关在家里,做了个无聊成就:我们将在树莓派上用 PowerShell 调用 Azure 上的一个 .NET Core 写的 API 来获取疫情数据。
疫情数据API
疫情数据来源于腾讯新闻的一个实时更新的页面,原始 API 地址为:
https://view.inews.qq.com/g2/getOnsInfo?name=wuwei_ww_cn_day_counts"
然而我们没有办法直接使用,因为它的返回居然不是规范的 JSON:
因此,我只能自己 996 一下,用 .NET Core 包装了腾讯的 API,做了规范化的输出,最终结果为:
我们只需要将腾讯 API 中的 data 字符串取出,反序列化为强类型 model 即可。
public class InfectionData
{
public DateTime Date { get; set; }
public int Confirm { get; set; }
public int Suspect { get; set; }
public int Dead { get; set; }
public int Heal { get; set; }
}
由于 System.Text.Json 福报非常多,我依旧使用老牌 Json.NET 搞定数据转换:
var data = await _httpClient.GetStringAsync(_settings.Value.DataSource);
var apiRes = JsonConvert.DeserializeObject<CoronavirusApiResponse>(data);
var infectionData = JsonConvert.DeserializeObject<IEnumerable<InfectionData>>(apiRes.Data);
代码地址:https://github.com/EdiWang/DotNet-Samples/tree/master/CoronavirusReport
其中用到的知识点为 HttpClient + Polly,这是 .NET Core 调用 REST API 最安全可靠的实践。
简单来说,就是为了调一个API,你得针对这个API建一个接口、一个实现,把 HttpClient 类型通过 services.AddHttpClient 加入 DI 传进来使用,最后利用 Polly 配置出错自动重试等异常处理。
public interface ICoronavirusApiClient
{
Task<IEnumerable<InfectionData>> GetInfectionDataAsync();
}
public class CoronavirusApiClient : ICoronavirusApiClient
{
private readonly HttpClient _httpClient;
private readonly ILogger<CoronavirusApiClient> _logger;
private readonly IOptions<AppSettings> _settings;
public CoronavirusApiClient(
HttpClient httpClient,
ILogger<CoronavirusApiClient> logger,
IOptions<AppSettings> settings)
{
_logger = logger;
_settings = settings;
_httpClient = httpClient;
}
public async Task<IEnumerable<InfectionData>> GetInfectionDataAsync()
{
var data = await _httpClient.GetStringAsync(_settings.Value.DataSource);
var apiRes = JsonConvert.DeserializeObject<CoronavirusApiResponse>(data);
var infectionData = JsonConvert.DeserializeObject<IEnumerable<InfectionData>>(apiRes.Data);
var infectionDataArray = infectionData as InfectionData[] ?? infectionData.ToArray();
_logger.LogInformation($"Got {infectionDataArray.Length} records from '{_settings.Value.DataSource}'");
return infectionDataArray;
}
}
services.AddHttpClient<ICoronavirusApiClient, CoronavirusApiClient>()
.AddTransientHttpErrorPolicy(builder =>
builder.WaitAndRetryAsync(3, retryCount =>
TimeSpan.FromSeconds(Math.Pow(2, retryCount)),
(result, span, retryCount, context) =>
{
_logger?.LogWarning($"Request failed with {result.Result.StatusCode}. Waiting {span} before next retry. Retry attempt {retryCount}/3.");
}));
至于为什么不能简单粗暴 new 一个 HttpClient 直接调API,推荐观看另一名 MVP Steve Gordon 的视频《Let's Talk HTTP in .NET Core》:https://www.youtube.com/watch?v=ojDxK_-I-To (咦,这是什么不存在的网站)
最后,我把处理好格式的的疫情 API 部署到了Azure国际版的 App Service 上。
https://covid19cn.azurewebsites.net/infectiondata
Linux居然有PowerShell?
由于 Windows 10 早已是一盘咖喱拌饭,导致在树莓派上最好用的系统只能是 Linux,而我这种微软系程序员显然不熟悉 bash,如果能用上 PowerShell 那就太好了!随着微软的开源、开放(Linux First, Windows Last)的态度,PowerShell 其实也和 .NET 一样,已经能够跨平台运行。
以树莓派官方系统 Raspbian 为例,安装 PowerShell 的方式如下:
sudo apt-get install libunwind8
wget https://github.com/PowerShell/PowerShell/releases/download/v7.0.0-rc.3/powershell-7.0.0-rc.3-linux-arm32.tar.gz
mkdir ~/powershell
tar -xvf ./powershell-7.0.0-rc.3-linux-arm32.tar.gz -C ~/powershell
sudo ln -s ~/powershell/pwsh /usr/bin/pwsh
sudo ln -s ~/powershell/pwsh /usr/local/bin/powershell
powershell
PowerShell 的最新发布可以关注官方 GitHub:https://github.com/PowerShell/PowerShell/releases
参考:https://www.hanselman.com/blog/InstallingPowerShellCoreOnARaspberryPiPoweredByNETCore.aspx
另外,如果你和我一样用树莓派4,那么 Ubuntu 19.10.1 作为64位系统,可以通过相同方法安装64位 PowerShell,感兴趣的可以尝试。
现在我们可以愉快的调用疫情数据了!PowerShell 自带一个 Invoke-RestMethod 方法专门用来调用 REST API,非常方便,996。而 Format-Table 则可以把返回的 JSON 对象直接用表格形式来展示,更不会 996!
Invoke-RestMethod 参考:https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod?view=powershell-7
当然,你在 Windows 咖喱味 PC 上也能用经典 PowerShell 做这件事: