1. 程式人生 > 其它 >Blazor Server 應用程式中進行 HTTP 請求

Blazor Server 應用程式中進行 HTTP 請求

翻譯自 Waqas Anwar 2021年5月4日的文章 《Making HTTP Requests in Blazor Server Apps》 [1]

Blazor Server 應用使用標準的 ASP.NET Core 應用程式,在服務端執行 .NET 程式碼。在 Blazor Server 應用程式中,我們可以像在 ASP.NET Core Web 應用程式中那樣,使用相同的方式訪問任意 .NET 庫或服務端功能。這其中的一項功能是,使用 HTTP Client 例項向第三方 Web API 傳送 HTTP 請求。在本教程中,我將向您展示建立 HTTP Client 例項的不同方法。另外,我還會向您展示如何在 Blazor Server 應用程式中使用第三方 API 來獲取和顯示資料。

下載原始碼[2]

第三方 Web API 概覽

我們將開發一個 Blazor Server 應用程式,該應用允許使用者在 Blazor 頁面元件上輸入國家程式碼和年份,然後我們將呼叫第三方 API 以獲取指定國家和年份的公共假期列表。我們使用的第三方 API 是 Nager.Date[3],它是一個全球公共假期 API。

這是一個非常簡單的 API,您可以輕鬆地在 Postman 中輸入以下 URL 測試此 API。

https://date.nager.at/api/v2/PublicHolidays/2021/CN

該 API 的響應是 JSON 格式的公共假期列表,如下所示:

從 Blazor Sever 應用程式開始

在 Visual Studio 2019 中建立一個 Blazor Server 應用程式,並新建一個名為 Models 的資料夾。在 Models 資料夾中新增以下兩個模型類,以對映上述 Holidays API 的請求和響應。

HolidayRequestModel.cs

public class HolidayRequestModel
{
    public string CountryCode { get; set; }
    public int Year { get; set; }
}

HolidayResponseModel.cs

public class HolidayResponseModel
{
    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; }
}

接下來,在 Pages 資料夾中建立一個新的 Razor 元件 HolidaysExplorer.razor 及其程式碼隱藏檔案 HolidaysExplorer.razor.cs。如果您想了解有關 Razor 元件和程式碼隱藏檔案的更多知識,可以閱讀我的文章《Blazor 元件入門指南》

HolidaysExplorer.razor.cs

public partial class HolidaysExplorer
{
    private HolidayRequestModel HolidaysModel = new HolidayRequestModel();
    private List<HolidayResponseModel> Holidays = new List<HolidayResponseModel>();
 
    [Inject]
    protected IHolidaysApiService HolidaysApiService { get; set; }
 
    private async Task HandleValidSubmit()
    {
        Holidays = await HolidaysApiService.GetHolidays(HolidaysModel);
    }
}

HolidaysModel 欄位是 HolidayRequestModel 類的一個例項,它將幫助我們建立一個簡單的表單來向用戶詢問國家程式碼和年份。下面的程式碼片段顯示了使用 HolidaysModel 物件建立的 Blazor 表單,其中 HandleValidSubmit 方法是使用 Blazor Form 的 OnValidSubmit 事件配置的,使用者提交表單時該方法將被呼叫。

<EditForm Model="@HolidaysModel" OnValidSubmit="@HandleValidSubmit" class="form-inline">
     
   <label class="ml-2">Country Code:</label>
   <InputText id="CountryCode" @bind-Value="HolidaysModel.CountryCode" class="form-control" />
     
   <label class="ml-2">Year:</label>
   <InputNumber id="Year" @bind-Value="HolidaysModel.Year" class="form-control" />
     
   <button class="btn btn-primary ml-2" type="submit">Submit</button>
     
</EditForm>

Holidays 列表用來顯示從第三方 API 返回的假期。我們需要使用一個 @foreach 迴圈迭代返回的假期來生成一個簡單的 bootstrap 表格。

@if (Holidays.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 Holidays)
          {
              <tr>
                 <td>@item.Date.Value.ToShortDateString()</td>
                 <td>@item.Name</td>
                 <td>@item.LocalName</td>
                 <td>@item.CountryCode</td>
                 <td>@item.Global</td>
              </tr>
          }
       </tbody>
    </table>
}

HolidaysExplorer.razor 檢視的完整程式碼如下:

HolidaysExplorer.razor

@page "/"
<h3>Holidays Explorer</h3>
<br />
 
<EditForm Model="@HolidaysModel" OnValidSubmit="@HandleValidSubmit" class="form-inline">
 
   <label class="ml-2">Country Code:</label>
   <InputText id="CountryCode" @bind-Value="HolidaysModel.CountryCode" class="form-control" />
 
   <label class="ml-2">Year:</label>
   <InputNumber id="Year" @bind-Value="HolidaysModel.Year" class="form-control" />
 
   <button class="btn btn-primary ml-2" type="submit">Submit</button>
 
</EditForm>
 
<br />
@if (Holidays.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 Holidays)
          {
              <tr>
                 <td>@item.Date.Value.ToShortDateString()</td>
                 <td>@item.Name</td>
                 <td>@item.LocalName</td>
                 <td>@item.CountryCode</td>
                 <td>@item.Global</td>
              </tr>
          }
       </tbody>
    </table>
}

此時如果您執行該應用程式,您將看到一個不顯示任何假期的簡單 HTML 表單。這是因為方法 HandleValidSubmit 是空的,我們還未呼叫任何 API 來獲取假期資料。

在 Blazor Server 應用程式中使用 IHttpClientFactory 建立 HttpClient

在 Blazor Server 應用程式中使用 HttpClient 請求第三方 API 有多種不同的方式,讓我們從一個基礎的示例開始,在該示例中我們使用 IHttpClientFactory 建立 HttpClient 物件。

在專案中建立一個 Services 資料夾,並建立如下的 IHolidaysApiService 介面。該介面只有一個方法 GetHolidays,它以 HolidayRequestModel 作為引數並返回 HolidayResponseModel 物件的列表。

IHolidaysApiService.cs

public interface IHolidaysApiService
{
    Task<List<HolidayResponseModel>> GetHolidays(HolidayRequestModel holidaysRequest);
}

接下來,在 Services 資料夾中建立一個 HolidaysApiService 類,實現上面的介面。

HolidaysApiService.cs

public class HolidaysApiService : IHolidaysApiService
{
    private readonly IHttpClientFactory _clientFactory;
 
    public HolidaysApiService(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }
 
    public async Task<List<HolidayResponseModel>> GetHolidays(HolidayRequestModel holidaysRequest)
    {
        var result = new List<HolidayResponseModel>();
 
        var url = string.Format("https://date.nager.at/api/v2/PublicHolidays/{0}/{1}", 
            holidaysRequest.Year, holidaysRequest.CountryCode);
 
        var request = new HttpRequestMessage(HttpMethod.Get, url);
        request.Headers.Add("Accept", "application/vnd.github.v3+json");
 
        var client = _clientFactory.CreateClient();
 
        var response = await client.SendAsync(request);
 
        if (response.IsSuccessStatusCode)
        {
            var stringResponse = await response.Content.ReadAsStringAsync();
 
            result = JsonSerializer.Deserialize<List<HolidayResponseModel>>(stringResponse,
                new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
        }
        else
        {
            result = Array.Empty<HolidayResponseModel>().ToList();
        }
 
        return result;
    }
}

在上面的 GetHolidays 方法中,我們首先為第三方 API 建立了一個 URL,並將國家程式碼和年份引數新增到 URL 中。

var url = string.Format("https://date.nager.at/api/v2/PublicHolidays/{0}/{1}", holidaysRequest.Year, holidaysRequest.CountryCode);

接下來,我們建立了 HttpRequestMessage 物件並配置它以向第三方 API URL 傳送 HTTP GET 請求。

var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("Accept", "application/vnd.github.v3+json");

可以使用依賴注入 (DI) 請求一個 IHttpClientFactory,這正是我們將其注入到前面類的建構函式的原因。下面這行程式碼使用 IHttpClientFactory 建立了一個 HttpClient 例項。

var client = _clientFactory.CreateClient();

有了 HttpClient 物件之後,我們簡單地呼叫它的 SendAsync 方法來發送一個 HTTP GET 請求。

var response = await client.SendAsync(request);

如果 API 呼叫成功,我們使用下面這行程式碼將其響應讀取為字串。

var stringResponse = await response.Content.ReadAsStringAsync();

最後,我們使用 JsonSerializer 類的 Deserialize 方法反序列化該響應。

result = JsonSerializer.Deserialize<List<HolidayResponseModel>>(stringResponse, 
   new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });

在測試該應用程式之前,我們需要在 Startup.cs 檔案中註冊 HolidaysApiService 服務。我們還需要呼叫 AddHttpClient 方法註冊 IHttpClientFactory

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
 
    services.AddSingleton<IHolidaysApiService, HolidaysApiService>();
 
    services.AddHttpClient();
}

執行應用程式並在文字框中提供任意國家程式碼和年份。點選 Submit 按鈕就會在後臺呼叫我們的 GetHolidays 方法,然後您應該能看到如下所示的公共假期列表。

在 Blazor Server 應用程式中建立命名 HttpClient 物件

上面的示例適用於您正在重構現有的應用程式,希望在不影響整個應用程式的情況下,在某些方法中使用 IHttpClientFactory 建立 HttpClient 物件的場景。如果您要建立一個全新的應用程式,或者您想要將建立 HttpClient 物件的方式集中化,那麼您必須使用命名 HttpClient

下面是建立命名 HTTP 客戶端的好處:

  1. 我們可以為每個 HttpClient 命名,並在應用程式啟動時指定與 HttpClient 相關的所有配置,而不是將配置分散在整個應用程式當中。
  2. 我們可以只配置一次命名的 HttpClient,並多次重用它呼叫一個特定 API 提供者的所有 API。
  3. 我們可以根據這些客戶端在應用程式不同區域的使用情況,配置多個不同配置的命名 HttpClient 物件。

我們可以在 Startup.cs 檔案的 ConfigureServices 方法中,使用前面用過的名為 AddHttpClient 方法指定一個命名的 HttpClient。

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
 
    services.AddSingleton<IHolidaysApiService, HolidaysApiService>();
 
    services.AddHttpClient("HolidaysApi", c =>
    {
        c.BaseAddress = new Uri("https://date.nager.at/");
        c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    });
}

我們需要指定客戶端的名稱(例如 HolidaysApi),我們還可以配置如上所示的 BaseAddressDefaultRequestHeaders 和其他屬性。

配置了命名 HttpClient 之後,我們可以使用相同的 CreateClient 方法在整個應用程式中建立 HttpClient 物件,不過這次我們需要指定想要建立哪個已命名的客戶端(例如 HolidaysApi)。

HolidaysApiService.cs

public class HolidaysApiService : IHolidaysApiService
{
    private readonly IHttpClientFactory _clientFactory;
 
    public HolidaysApiService(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }
 
    public async Task<List<HolidayResponseModel>> GetHolidays(HolidayRequestModel holidaysRequest)
    {
        var result = new List<HolidayResponseModel>();
 
        var url = string.Format("api/v2/PublicHolidays/{0}/{1}", 
            holidaysRequest.Year, holidaysRequest.CountryCode);
 
        var request = new HttpRequestMessage(HttpMethod.Get, url);
 
        var client = _clientFactory.CreateClient("HolidaysApi");
 
        var response = await client.SendAsync(request);
 
        if (response.IsSuccessStatusCode)
        {
            var stringResponse = await response.Content.ReadAsStringAsync();
 
            result = JsonSerializer.Deserialize<List<HolidayResponseModel>>(stringResponse,
                new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
        }
        else
        {
            result = Array.Empty<HolidayResponseModel>().ToList();
        }
 
        return result;
    }
}

我們在 CreateClient 方法中傳遞的名稱(比如 HolidaysApi)必須與我們在 Startup.cs 檔案中配置的名稱一致。每次呼叫 CreateClient 方法時,都會為我們建立一個新的 HttpClient 例項。

另外,我們不需要在請求的 URL 中指定 API 主機名稱,因為我們在 Startup.cs 檔案中已經指定過基地址了。

再次執行應用程式並提供國家程式碼和年份值,您應該能看到以下公共假期列表。

在 Blazor Server 應用程式中建立型別化 HttpClient 物件

建立和使用 HttpClient 物件的第三種選擇是使用型別化客戶端。這種客戶端具有以下好處:

  1. 它們提供與命名客戶端同樣的功能,但無需使用字串作為鍵。
  2. 它們在使用客戶端時提供智慧感知和編譯器幫助。
  3. 它們提供了一個單一的儲存單元來配置特定的 HttpClient 並與之互動。例如,我們可以配置針對 Facebook API 的一個特定終端的一個型別化 HttpClient,而且該 HttpClient 可以封裝使用該特定終端所需的所有邏輯。
  4. 它們與依賴注入 (DI) 一起使用,可以在需要的地方注入。

要配置型別化的 HTTPClient,我們需要在 Startup.cs 檔案中使用相同的 AddHttpClient 方法註冊它,但這一次,我們需要傳遞我們的服務名稱 HolidaysApiService 作為它的型別。

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
 
    services.AddSingleton<IHolidaysApiService, HolidaysApiService>();
 
    services.AddHttpClient<HolidaysApiService>();
}

在上面的程式碼片段中,HTTP 客戶端和我們的服務 HolidaysApiService 都將註冊為瞬時客戶端和服務。這將允許我們在服務的建構函式中傳遞 HttpClient,如以下程式碼片段所示。請注意,HttpClient 是如何公開為服務的 public 屬性的。

HolidaysApiService.cs

public class HolidaysApiService : IHolidaysApiService
{
    public HttpClient Client { get; }
 
    public HolidaysApiService(HttpClient client)
    {
        client.BaseAddress = new Uri("https://date.nager.at/");
        client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
        Client = client;
    }
 
    public async Task<List<HolidayResponseModel>> GetHolidays(HolidayRequestModel holidaysRequest)
    {
        var result = new List<HolidayResponseModel>();
 
        var url = string.Format("api/v2/PublicHolidays/{0}/{1}",
            holidaysRequest.Year, holidaysRequest.CountryCode);
 
        var response = await Client.GetAsync(url);
 
        if (response.IsSuccessStatusCode)
        {
            var stringResponse = await response.Content.ReadAsStringAsync();
 
            result = JsonSerializer.Deserialize<List<HolidayResponseModel>>(stringResponse,
                new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
        }
        else
        {
            result = Array.Empty<HolidayResponseModel>().ToList();
        }
 
        return result;
    }
}

型別化客戶端的配置也可以不在型別化客戶端的建構函式中指定,而在註冊期間在 Startup.cs 檔案的 ConfigureServices 方法中指定。

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor(); 
 
    services.AddHttpClient<IHolidaysApiService, HolidaysApiService>(c =>
    {
        c.BaseAddress = new Uri("https://date.nager.at/");
        c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    });
}

如果您使用的是這種方式,則無需單獨註冊您的服務。您可以從 ConfigureServices 方法中刪除下面這行程式碼。

services.AddSingleton<IHolidaysApiService, HolidaysApiService>();

可以將 HttpClient 物件密封在一個型別化客戶端中,而不公開為 public 屬性。然後,我們可以在服務內部的任意方法中使用這個客戶端。

public class HolidaysApiService : IHolidaysApiService
{
    private readonly HttpClient _httpClient;
 
    public HolidaysApiService(HttpClient client)
    {
        _httpClient = client;
    }
 
    public async Task<List<HolidayResponseModel>> GetHolidays(HolidayRequestModel holidaysRequest)
    {
        var result = new List<HolidayResponseModel>();
 
        var url = string.Format("api/v2/PublicHolidays/{0}/{1}",
            holidaysRequest.Year, holidaysRequest.CountryCode);
 
        var response = await _httpClient.GetAsync(url);
 
        if (response.IsSuccessStatusCode)
        {
            var stringResponse = await response.Content.ReadAsStringAsync();
 
            result = JsonSerializer.Deserialize<List<HolidayResponseModel>>(stringResponse,
                new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
        }
        else
        {
            result = Array.Empty<HolidayResponseModel>().ToList();
        }
 
        return result;
    }
}

再次執行應用程式,並提供國家程式碼和年份值,您應該能夠看到以下公共假期列表。

總結

在本文中,我介紹了在 Blazor Server 應用程式中建立和使用 HTTP 客戶端的不同技術。這裡提到的大部分技術也可以在 ASP.NET Core 應用程式中使用,因為 Blazor Server 應用程式是構建在 ASP.NET Core 基礎架構之上的。在我的下篇文章 《Making HTTP Requests in Blazor WebAssembly Apps》 中,我將嘗試介紹 HTTP 客戶端在 Blazor WebAssembly 應用程式中的建立和使用。


相關閱讀:

作者 : Waqas Anwar
翻譯 : 技術譯站
連結 : 英文原文


  1. https://www.ezzylearning.net/tutorial/making-http-requests-in-blazor-server-apps Making HTTP Requests in Blazor Server Apps ↩︎

  2. https://github.com/ezzylearning/BlazorServerWebAPIsDemo ↩︎

  3. https://date.nager.at/ ↩︎

© 轉載請標明出處 https://www.cnblogs.com/ittranslator

不做標題黨,只分享技術乾貨

公眾號『技術譯站』,歡迎掃碼關注