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.

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

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.

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

Blazor Server Apps’ creation of HttpClient using IHttpClientFactory

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

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.

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:

  1. Without requiring the use of strings as keys, they offer the same functionality as named clients.
  2. When consuming clients, they offer IntelliSense and compiler assistance.
  3. 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.
  4. 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;
    }
}
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;
    } 
}

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.