第三方 API 概览
我们将开发一个允许用户输入国家代码和年份的应用程序,然后我们将调用第三方 API 来获取该特定国家在该特定年份的公共假期列表。我们将使用的第三方 API 称为 Nager.Date,这是一个全球公共假期 API。
在这里插入图片描述
这是一个非常简单的 API,您可以通过输入以下 URL 在 Postman 中轻松测试此 API。
https://date.nager.at/api/v2/PublicHolidays/2020/US
该 API 的响应是 JSON 格式的公共假期列表,如下所示:
在这里插入图片描述
了解 HttpClient 对象
允许我们在 ASP.NET Core 应用程序中使用第三方 API 的最常见和众所周知的类是 HttpClient 类。此类使我们能够向第三方 API 发送 HTTP 请求并接收从这些 API 返回的 HTTP 响应。 HttpClient 的每个实例都维护着自己的连接池,这使得它可以将自己的请求与其他 HttpClient 实例执行的请求隔离开来。此类还充当更特定 HTTP 客户端的基类。例如,您可以创建 FacebookHttpClient 或 TwitterHttpClient 作为基本 HttpClient 的子类,并且可以使用这些特定的 HTTP 客户端与 Facebook 和 Twitter API 进行通信。
建议创建一个 HttpClient 实例并在整个应用程序生命周期中重复使用它。这是因为为每个请求实例化一个新的 HttpClient 实例很容易耗尽重负载下可用的套接字数量。这主要是因为当 HttpClient 对象被释放,底层套接字不会立即释放。 您可以阅读这篇精彩的博客文章您使用 HttpClient 错误,这会破坏您的软件的稳定性,以获取有关我刚刚提到的问题的更多信息。
在 ASP.NET Core 中使用 HttpClient
正如我上面提到的,我们将创建一个应用程序,允许用户查看任何国家/地区的公共假期列表。让我们创建一个 ASP.NET Core MVC Web 应用程序并创建以下接口。这个接口只有一个 GetHolidays 方法,它有两个参数 countryCode 和 year,我们很快就会从用户那里收到。
public interface IHolidaysApiService
{
Task<List<HolidayModel>> GetHolidays(string countryCode, int year);
}
上面的 GetHolidays 方法返回一个 HolidayModel 列表,它是一个模型类,具有与 Nager.Date API 的响应映射的属性。
public class HolidayModel
{
public string Name { get; set; }
public string LocalName { get; set; }
public DateTime? Date { get; set; }
public string CountryCode { get; set; }
public bool Global { get; set; }
}
接下来,我们需要实现一个 HolidaysApiService 类,该类将实现上面声明的 IHolidaysApiService。请注意我是如何在类中声明私有和静态 HttpClient 变量的,以及它是如何在类的静态构造函数中定义的。这是 Microsoft 官方文档中提到的创建 HttpClient 实例的推荐方法。
public class HolidaysApiService : IHolidaysApiService
{
private static readonly HttpClient client;
static HolidaysApiService()
{
client = new HttpClient()
{
BaseAddress = new Uri("https://date.nager.at")
};
}
}
接下来我们需要定义 GetHolidays 方法,如下所示:
public async Task<List<HolidayModel>> GetHolidays(string countryCode, int year)
{
var url = string.Format("/api/v2/PublicHolidays/{0}/{1}", year, countryCode);
var result = new List<HolidayModel>();
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var stringResponse = await response.Content.ReadAsStringAsync();
result = JsonSerializer.Deserialize<List<HolidayModel>>(stringResponse,
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
}
else
{
throw new HttpRequestException(response.ReasonPhrase);
}
return result;
}
上面的方法发生了很多事情,所以让我详细解释一下:
第一行是构建 Nager.Date API 的 URL 并使用 year 和 countryCode 参数
var url = string.Format("/api/v2/PublicHolidays/{0}/{1}", year, countryCode);
接下来,我们将使用 GetAsync 方法进行 API 调用,该方法将 GET 请求作为异步操作发送到指定的 Uri。该方法返回 System.Net.Http.HttpResponseMessage 对象,该对象表示包含状态代码和数据的 HTTP 响应消息。
var response = await client.GetAsync(url);
接下来,我们调用 ReadAsStringAsync 方法将 HTTP 内容序列化为字符串
var stringResponse = await response.Content.ReadAsStringAsync();
最后,我们使用 JsonSerializer 将 JSON 响应字符串反序列化为 HolidayModel 对象列表。
result = JsonSerializer.Deserialize<List<HolidayModel>>(stringResponse,
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
这就是我们使用第三方公共假期 API 所需的全部内容。要使用我们的 HolidaysApiService,我们需要首先在 Startup.cs 类中注册我们的服务。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<IHolidaysApiService, HolidaysApiService>();
}
接下来,我们可以在 HomeController 中注入我们的 HolidaysApiService 并通过传递我们将在 Index action 方法中接收的 countryCode 和 year 参数来调用 GetHolidays 方法。
public class HomeController : Controller
{
private readonly IHolidaysApiService _holidaysApiService;
public HomeController(IHolidaysApiService holidaysApiService)
{
_holidaysApiService = holidaysApiService;
}
public async Task<IActionResult> Index(string countryCode, int year)
{
List<HolidayModel> holidays = new List<HolidayModel>();
holidays = await _holidaysApiService.GetHolidays(countryCode, year);
return View(holidays);
}
}
最后,我们需要一个 Razor 视图来创建一个表单,用户将在其中输入国家代码和年份。表单将提交给上述 Index 操作,然后该操作将调用 GetHolidays 方法。这是 Index.cshtml Razor 视图的代码,显示了一个 HTML 表单和一个用于显示公共假期的表格。
@model List<HolidayModel>
@{
ViewData["Title"] = "Home Page";
}
<div>
<h3 class="display-4">Public Holidays Finder</h3>
<center>
<form asp-controller="Home" asp-action="Index">
<table>
<tr>
<td>Country Code: </td>
<td><input type="text" id="txtCountryCode" name="CountryCode" /></td>
<td>Year: </td>
<td><input type="text" id="txtYear" name="Year" /></td>
<td><input type="submit" value="Submit" /></td>
</tr>
</table>
<hr />
</form>
</center>
@if (Model != null && Model.Count > 0)
{
<table class="table table-bordered table-striped table-sm">
<thead>
<tr>
<th>Date</th>
<th>Name</th>
<th>Local Name</th>
<th>Country Code</th>
<th>Global</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>@item.Date.Value.ToShortDateString()</td>
<td>@Html.DisplayFor(modelItem => item.Name)</td>
<td>@Html.DisplayFor(modelItem => item.LocalName)</td>
<td>@Html.DisplayFor(modelItem => item.CountryCode)</td>
<td>@Html.DisplayFor(modelItem => item.Global)</td>
</tr>
}
</tbody>
</table>
}
</div>
现在是时候测试我们的应用程序,看看我们是否能够使用第三方 API。在 Visual Studio 中按 F5,您将看到类似于以下内容的页面。您可以输入国家代码,例如美国、德国等,以及一年,例如2021,然后单击“提交”按钮,如果一切顺利,您将看到我们的代码调用第三方 API,从 API 中获取公共假期列表并将其显示在页面上。
在这里插入图片描述
使用 IHttpClientFactory 管理 HttpClient 对象
为了使 HttpClient 实例易于管理,并避免上述套接字耗尽问题,.NET Core 2.1 引入了 IHttpClientFactory 接口,可用于通过依赖注入 (DI) 在应用程序中配置和创建 HttpClient 实例。为了使用 IHttpClientFactory,我们可以通过调用 AddHttpClient(IServiceCollection) 在 Startup.cs 文件中注册它。
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<IHolidaysApiService, HolidaysApiService>();
services.AddHttpClient("PublicHolidaysApi", c => c.BaseAddress = new Uri("https://date.nager.at"));
}
可以使用 AddHttpClient 方法注册多个具有不同名称的 HTTP 客户端。 AddHttpClient 方法的第一个参数是客户端的名称,第二个参数是将配置 HttpClient 的 Lamba 表达式。在上面的示例中,我使用要使用此特定 HTTP 客户端调用的第三方 API 的 URL 来设置 BaseAddress 属性。
一旦 HTTP 客户端被注册,我们就可以在我们的控制器和服务中注入 IHttpClientFactory 并调用它的 CreateClient 方法来创建我们想要在我们的代码中使用的特定 HTTP 客户端对象。 CreateClient 方法需要您要创建的 HTTP 客户端的名称,如下所示:
public class HolidaysApiService : IHolidaysApiService
{
private readonly HttpClient client;
public HolidaysApiService(IHttpClientFactory clientFactory)
{
client = clientFactory.CreateClient("PublicHolidaysApi");
}
public async Task<List<HolidayModel>> GetHolidays(string countryCode, int year)
{
var url = string.Format("/api/v2/PublicHolidays/{0}/{1}", year, countryCode);
var result = new List<HolidayModel>();
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var stringResponse = await response.Content.ReadAsStringAsync();
result = JsonSerializer.Deserialize<List<HolidayModel>>(stringResponse,
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
}
else
{
throw new HttpRequestException(response.ReasonPhrase);
}
return result;
}
}
总结概括
在本文中,我向您概述了 HttpClient,并提供了直接或使用 IHttpClientFactory 创建 HttpClient 对象的示例。我还向您展示了一个使用 HttpClient 调用第三方 Web API 的示例。希望您现在熟悉 HttpClient 对象及其用法,并且可以放心地开始在您的项目中使用它。