How to Make HTTP Request in Blazor Apps – Windows ASP.NET Core Hosting 2024 | Review and Comparison

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.

It is a very simple API and you can easily test this API in Postman by entering the following URL.

The list of public holidays returned by this API is displayed below in JSON format:

Getting Started with 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; }
}

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; }
}
Next, create the code-behind file HolidaysExplorer.razor.cs and a new Razor component called HolidaysExplorer.razor in the Pages folder.

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);
    }
}
We can easily create a straightforward form that asks the user for the Country Code and the Year by using the HolidaysModel field, which is an instance of the HolidayRequestModel class. The Blazor form built with the HolidaysModel object is displayed in the following snippet of code. When the user submits the form, the HandleValidSubmit method, which is configured with the Blazor Form’s OnValidSubmit event, is called.
<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>
The holidays returned from the third-party API will be shown in the Holidays list. By iterating the holidays with a straightforward @foreach loop, we must create a straightforward bootstrap table.
@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>
}

The complete code of HolidaysExplorer.razor view is shown below.

@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>
}
You will see a plain HTML form without any holidays if you run the app at this point. This is due to the fact that the method HandleValidSubmit is empty and that we have not yet made an API call to retrieve any holiday data.

Creating HttpClient using IHttpClientFactory in Blazor Server Apps

HttpClient can be used in Blazor server apps in a variety of ways, so let’s start with a simple example in which we create a HttpClient object using the IHttpClientFactory.

Make the following IHolidaysApiService interface in the project’s Services folder. The interface only has one method, GetHolidays, which returns a list of HolidayResponseModel objects and accepts a HolidayRequestModel as a parameter.

IHolidaysApiService.cs

public interface IHolidaysApiService
{
    Task<List<HolidayResponseModel>> GetHolidays(HolidayRequestModel holidaysRequest);
}
Then, in the Services folder, make a class called HolidaysApiService and implement the aforementioned interface.

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;
    }
}
In the aforementioned GetHolidays method, we first made a URL for a third-party API and added the year and country code parameters to the URL.
var url = string.Format("https://date.nager.at/api/v2/PublicHolidays/{0}/{1}", holidaysRequest.Year, holidaysRequest.CountryCode);
After that, we created an HTTPRequestMessage object and set it up to send a GET request to an API URL of a third party.
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);

If the API call is successful, we are reading the response as a string using the following line.

var stringResponse = await response.Content.ReadAsStringAsync();
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();
}
Launch the application and enter the year and any desired country code in the text fields. After pressing the Submit button, our GetHolidays method should be called in the background, displaying the list of public holidays as shown below.

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:

  1. 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.
  2. The named HttpClient can be configured only once and then used repeatedly to call APIs from a specific API provider.
  3. 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.

Creating Typed HttpClient objects in Blazor Server Apps

Utilizing Typed clients is the third method for producing and utilizing HttpClient objects. The advantages for these clients are as follows:

  1. They provide the same capabilities as named clients without the need to use strings as keys.
  2. They provide IntelliSense and compiler help when consuming clients.
  3. They provide a single location to configure and interact with a particular HttpClient. For example, we can configure a typed HttpClient specific to a particular endpoint of Facebook API, and that HttpClient can encapsulate all the logic required to use that particular endpoint.
  4. They work with Dependency Inject (DI) and can be injected where required.

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;
    }
}

Instead of in the constructor of the typed client, the configuration for a typed client can be specified during registration in the ConfigureServices method of the Startup.cs file.

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");
    });
}

You don’t need to register your service separately if you’re using this method. The following line can be taken out of the ConfigureServices method.

services.AddSingleton<IHolidaysApiService, HolidaysApiService>();

Instead of being made available as a public property, the HttpClient object can be contained within a typed client. Then, we can employ this client internally in any service model.

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;
    }
}

The list of public holidays should appear after you rerun the application and enter the country code and year values.

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.