Why should you use IHttpClientFactory

The main reason to use IHttpClientFactory
instead of HttpClient directly is to manage the life cycle of HttpClient instances. HttpClient was designed to be instantiated once and reused throughout the application life cycle. If you continually instantiate new HttpClients you may unnecessarily be squandering resources.

Another use case is for simplicities sake, you can define all Named and Typed clients in the Program.cs file and use them throughout the application via Dependency Injection.

Benefits of IHttpClientFactory

  1. Centralized location for Named/Typed HttpClient instances in Program.cs.
  2. Integrates with Polly for transient fault handling.
  3. Avoid common DNS problems by managing HttpClient lifetimes.
  4. Adds logging for all requests sent through clients created by the factory.
  5. Manage the lifetime of HttpMessageHandler to avoid the mentioned problems/issues that can occur when managing HttpClient lifetimes yourself.

What you may have done in the past

Here you can see we instantiate HttpClient directly in our method. This approach is not advisable for the reasons mentioned above.

				
					 public async Task<List<RecipeViewModel>> GetAllRecipes()
    {
        HttpClient client = new HttpClient();
        var results = await _httpClient?.GetFromJsonAsync<IEnumerable<RecipeViewModel>>("/Data/recipes");
        return results.ToList();
    }
				
			

Lets see below how to implement this better with IHttpClientFactory

How to Implement a Typed Client with Blazor Web Assembly

RecipeViewModel.cs We will use this model in our IRecipeService example
				
					public class RecipeViewModel
{
    public int Rid { get; set; }
    public string? Title { get; set; }
    public byte[]? ImageData { get; set; }
    public string? ImageFileName { get; set; }
    public string? ImageFormat { get; set; }
    public int? Duration { get; set; }
    public DateTime? CreatedDateTime { get; set; }
    public string? CreatedBy { get; set; }
    public DateTime? ModifiedDateTime { get; set; }
    public string? ModifiedBy { get; set; }
    public bool? IsDisabled { get; set; }
    public short? Rating { get; set; }
    public string? Description { get; set; }
}
				
			

IRecipeService.cs

We create an interface and a service for fetching data from an external API and map it to a view model. The methods are self explanatory.

				
					public interface IRecipeService
    {
        Task CreateRecipe(RecipeViewModel recipe);
        Task UpdateRecipe(RecipeViewModel recipe);
        Task<List<RecipeViewModel>> GetAllRecipes();
        Task DeleteRecipe(RecipeViewModel recipe);
    }

    public class RecipeService : IRecipeService
    {
        private readonly HttpClient _httpClient;
        public RecipeService(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }
       
        public async Task CreateRecipe(RecipeViewModel recipe)
        {
            var result = await _httpClient?.PostAsJsonAsync("/data/create-recipe", recipe);
            if (!result.IsSuccessStatusCode)
            {
                throw new Exception("Failed to save. Invalid Http Status Code");
            }
        }
      
        public async Task UpdateRecipe(RecipeViewModel recipe)
        {
            var result = await _httpClient?.PutAsJsonAsync("/data/update-recipe", recipe);
            if (!result.IsSuccessStatusCode)
            {
                throw new Exception("Failed to save. Invalid Http Status Code");
            }
        }
       
        public async Task<List<RecipeViewModel>> GetAllRecipes()
        {
            var results = await _httpClient?.GetFromJsonAsync<IEnumerable<RecipeViewModel>>("/Data/recipes");
            return results.ToList();
        }
        
        public async Task DeleteRecipe(RecipeViewModel recipe)
        {
            await _httpClient?.PostAsJsonAsync("/data/delete-recipe", recipe);
        }
				
			
Program.cs Here we create our Typed HttpClient that implements the IRecipeService, making it available throughout the application via Dependency Injection.
				
					var builder = WebAssemblyHostBuilder.CreateDefault(args);

    builder.RootComponents.Add<App>("#app");
    builder.RootComponents.Add<HeadOutlet>("head::after");

    builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
    builder.Services.AddHttpClient<IRecipeService, RecipeService>().ConfigureHttpClient(x=>x.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
    
    await builder.Build().RunAsync();
				
			

Using a Typed Client in our Blazor Component

ListRecipes.razor.cs Here we use the injected IRecipeService to fetch data from an external API. From there you can use the AllRecipes object throughout the Razor component.
				
					    public partial class ListRecipes
    {
        [Inject]
        private IRecipeService? RecipeService { get; set; }
        private List<RecipeViewModel>? AllRecipes { get; set; }
            
        protected override async Task OnInitializedAsync()
        {
            AllRecipes = await RecipeService?.GetAllRecipes();
        }
    }
				
			

This concludes our tutorial of using IHttpClientFactory with Blazor Web Assembly using a Typed Client. I hope you found this useful. Feel free to leave feedback below.

Want to learn more?