Blazor server apps run.NET code on the server using the common ASP.NET Core application. Similar to ASP.NET Core web applications, these apps allow us to access any.NET library or server-side feature. Utilizing HTTP Client instances to send HTTP requests to external Web APIs is one such feature. I’ll demonstrate various methods for creating HTTP Client instances in this tutorial. Additionally, I’ll demonstrate how to use a third-party API in Blazor Server Apps to fetch and display data.
Overview of Third Party Web API
We will create a Blazor server app that will enable users to enter a country code and a year on a Blazor page component. We will then make a third-party API call to retrieve the country’s list of public holidays for that specific year. We will use the Nager.Date third-party API, which is a global public holidays API.
By entering the following URL in Postman, you can quickly test this API because it is so straightforward.
https://date.nager.at/api/v2/PublicHolidays/2020/US
The response of this API is the list of public holidays in JSON format as shown below:
How to Use the Blazor Sever App
In Visual Studio 2019, create a Blazor Server App and a folder called Models. To map the Holidays API request and response depicted above, add the following two model classes to the Models folder.
HolidayRequestModel.cs
public class HolidayRequestModel { public string CountryCode { get; set; } public int Year { get; set; } }
HolidayRequestModel.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; }
}
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); } }
<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>
@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
Blazor Server Apps’ creation of HttpClient using IHttpClientFactory
IHolidaysApiService.cs
HolidaysApiService.cs
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");
Dependency injection (DI) can be used to request an IHttpClientFactory, which is why we are injecting it in the constructor of the aforementioned class. To create an instance of an HTTP client, use the following line’s IHttpClientFactory.
var
client = _clientFactory.CreateClient();
To send an HTTP GET request once the HttpClient object is available, we simply call its SendAsync method.
var response = await client.SendAsync(request);
Using the Deserialize method of the JsonSerializer class, we are finally deserializing the response.
result = JsonSerializer.Deserialize<List<HolidayResponseModel>>(stringResponse, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
We must first register HolidaysApiService in the Startup.cs file before we can test our app. Additionally, we must use the AddHttpClient method to register the IHttpClientFactory.
Startup.cs
public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); services.AddSingleton<IHolidaysApiService, HolidaysApiService>(); services.AddHttpClient(); }
Creating Named HttpClient objects in Blazor Server Apps
The aforementioned example is useful when you want to use IHttpClientFactory to create HttpClient objects in a few methods of an existing application without changing the entire application. Use named HTTP clients if you’re developing a new application or want to centralize the creation of HttpClient objects.
The advantages of designing named HTTP clients are as follows:
- Instead of having configurations scattered throughout the application, we can give each HttpClient a name and specify all HttpClient-related configurations at the application’s startup.
- The named HttpClient can be configured only once and then used repeatedly to call APIs from a specific API provider.
- Depending on how these clients are used throughout the application, we can configure multiple named HttpClient objects with various configurations.
Using the AddHttpClient method’s name, we can specify a named client in the ConfigureServices method of the Startup.cs file.
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"); }); }
The client’s name, such as HolidaysApi, must be specified. In addition, we can configure the BaseAddress, DefaultRequestHeaders, and other properties as displayed above.
Once the named HttpClient is set up, we can use the same CreateClient method to create HttpClient objects throughout the application, but this time we must specify which named client, for example, HolidaysApi, we want to create.
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; } }
The name, for instance, HolidaysApi, that we specify in the CreateClient method must match the name that we specify in the Startup.cs file. A new instance of HttpClient is created for us each time a CreateClient method is called.
Additionally, since we already specified the base address in the Startup.cs file, we don’t need to include the API hostname in the Request URL.
The list of public holidays should appear after you rerun the application and enter the country code and year values.
Blazor Server Apps: Typed HttpClient objects creation
The third option for creating and using HttpClient objects is to use Typed clients. These clients have the following benefits:
- Without requiring the use of strings as keys, they offer the same functionality as named clients.
- When consuming clients, they offer IntelliSense and compiler assistance.
- They offer a centralized location to set up and communicate with a specific HttpClient. For instance, we can set up a typed HTTP client that is specific to a certain Facebook API endpoint, and that HTTP client can contain all the logic necessary to use that specific endpoint.
- They can be injected when necessary and work with Dependency Inject (DI).
The same AddHttpClient method must be used in Startup.cs to register a typed HTTPClient, but this time we must pass the service name HolidaysApiService as the type.
Startup.cs
public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); services.AddSingleton<IHolidaysApiService, HolidaysApiService>(); services.AddHttpClient<HolidaysApiService>(); }
The HTTP client and our service HolidaysApiService will both be registered as transient clients and services in the code snippet above. As shown in the following code snippet, this will enable us to pass the HttpClient in the service’s constructor. Take note of how the HttpClient is displayed as the service’s public property.
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
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"); }); }
services.AddSingleton<IHolidaysApiService, HolidaysApiService>();
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; } }
Run the application once again and provide the country code and year values and you should be able to see the list of public holidays.
Summary
I discussed various methods for building and utilizing HTTP clients in Blazor Server Apps in this tutorial. Due to Blazor Server Apps’ foundation in ASP.NET Core infrastructure, the majority of the techniques discussed here are also applicable to ASP.NET Core applications.