In .Net Core applications, HttpClient instance should be managed by HttpClienFactory with the support of Dependency Injection(DI). Blazor Assembly applications also have those capabilities to use the
HttpClient object in an efficient manner. The techniques provided by the .Net framework are:
- Register HttpClient Object Explicitly In DI(Dependency Injection Service)
- Named Client
- Type Client
- HttpRequestMessage Object
Create Blazor WebAssembly Sample App:
To follow up on the steps let's begin by creating a sample Blazor WebAssembly application. Use any of the editors of your choice most recommended are like VisualStudio 2019(Support .Net 3.0 plus) or Visual Studio Code.
Third-Party Rest API:
To show sample implementation of the Blazor WebAssembly application here I'm going to use external free Rest API for our demos - "https://jsonplaceholder.typicode.com/todos"
Register HttpClient Object Explicity In DI(Technique - 1):
Initialize HttpClient object with a configuration like 'domain', 'default headers', etc in the Dependency Injection services. After that, we can inject the HttpClient object on *.razor(any Blazor Component), this injects implicitly done in the context of the HttpClientFactory. The only negative impact of the approach is when we have multiple different domain APIs to consume this approach fails to work. Because the last HttpClient object registration will override all other HttpClient registration. So this technique good when our application uses single domain API endpoints.
On creating our sample Blazor WebAssembly application we will get some startup boiler code with few pages. In those pages, in 'fetchdata' page this HttpClient approach is used by default. Let's explore it now.
Program.cs:
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });The HttpClient object registered in DI service as 'AddScoped'(object lives for entire user request). Registered our localhost URL(This technique code is by default generated code so I'm not replacing any of the code in this technique for remaining techniques I will use external 'todos' rest endpoint).
Pages/FetchData.razor:(Blazor Page Component)
@page "/fetchdata" @inject HttpClient Http <!-- some code hidden for display purpose --> @code { protected override async Task OnInitializedAsync() { forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json"); } }
- (Line: 1) '@page' directive to declare the route for the page
- (Line: 2)'@inject' directive to inject our class in razor files, here we injecting HttpClient object, typically implicitly done by IHttpClientFacotry.
- (Line: 6-8) 'OnInitializedAsync' Blazor WebAssembly life cycle method to prefetch some data at the time of the application loading. 'GetFromJsonAsync' method to invoke a get method by taking endpoint as a URL. The 'GetFromJsonAsync' extension method comes with a new library 'System.Net.Http.JSon' which by default installed from .Net Core 3.2.
Now let's try to register one more HttpClient object with 'todos' endpoint domain to it and check the behavior of it.
Program.cs:
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("https://jsonplaceholder.typicode.com/") });By doing this last registered HttpClient will override all others(implemented with the same technique) above it. Now run the application and confirm the override scenario as below.
Named Client:(Technique - 2)
The HttpClient object will be generated or invoked with the 'names'. So while registering the client we have to specify the 'name' for the HttpClient. Utilizing the same 'name' have to be used to create an HttpClient object. This approach supports multiple HttpClient registrations with their respective names.
Program.cs:
builder.Services.AddHttpClient("JsonPlaceHolderClient",client => { client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/"); });
- The 'JsonPlaceHolderClient' is the name for our HttpClient object.
Models/Todos.cs:
namespace BzWassem.ClientTypes.Models { public class Todos { public int UserId { get; set; } public int Id { get; set; } public string Title { get; set; } public bool Completed { get; set; } } }Now let's create a Blazor page to implement the 'Named' HttpClient object as follow.
Pages/NamedClient.razor:
@page "/named-client" @using BzWassem.ClientTypes.Models; @inject IHttpClientFactory _clientFacotry; <h3>NamedClient</h3> <div> @foreach (var todo in todos) { <p>@todo.Title</p> } </div> @code { private List<Todos> todos = new List<Todos>(); protected override async Task OnInitializedAsync() { var httpClient = _clientFacotry.CreateClient("JsonPlaceHolderClient"); todos = await httpClient.GetFromJsonAsync<List<Todos>>("/todos"); } }
- (Line: 3) Injected IHttpClientFactory on the Blazor page
- (Line: 18) creating a HttpClient object from IHttpClientFactory using the client registered name(eg: JsonPlaceHolderClient).
Typed Client:(Technique - 3)
Each external API domain will have a separate class to inject the HttpClient object. This class will be registered as the type in DI services.
Clients/JsonPlaceHolderClient.cs:
using BzWassem.ClientTypes.Models; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Json; using System.Threading.Tasks; namespace BzWassem.ClientTypes.Clients { public class JsonPlaceHolderClient { private HttpClient _httpClient; public JsonPlaceHolderClient(HttpClient httpClient) { _httpClient = httpClient; } public async Task<List<Todos>> GetTodos() { return await _httpClient.GetFromJsonAsync<List<Todos>>("/todos"); } } }
- The HttpClient object injected into the 'JsonPlaceHolderClient' file, this file act as a type for registration in DI.
builder.Services.AddHttpClient<JsonPlaceHolderClient>(client => { client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/"); });
- Registered HttpClient with its type as 'JsonPlaceHolderClient', this makes 'JsonPlaceHolderClient' as injectable instance.
Pages/TypeClient.razor:
@page "/typed-client" @using BzWassem.ClientTypes.Clients @using BzWassem.ClientTypes.Models @inject JsonPlaceHolderClient _jsonPlaceHolderClient; <h3>TypedClient</h3> <div> @foreach (var todo in todos) { <p>@todo.Title</p> } </div> @code { protected List<Todos> todos = new List<Todos>(); protected override async Task OnInitializedAsync() { todos = await _jsonPlaceHolderClient.GetTodos(); } }
- (Line: 4) Injected the Typed Client into the Blazor page.
HttpRequestMessage Object:(Technique - 4)
In this approach HttpRequestMessage object will be used to configure settings like 'domain', 'headers','payload', etc, this object will be used by the HttpClient object to invoke or consume the rest API.
Program.cs:
builder.Services.AddHttpClient();
- Simple registration of HttpClient this makes IHttpClientFactory can be injectable service.
Pages/RequestMessageType.razor:
@page "/request-message" @using BzWassem.ClientTypes.Models @inject IHttpClientFactory _httpClientFicatory; <h3>RequestMessageType</h3> <div> @foreach (var todo in todos) { <p>@todo.Title</p> } </div> @code { private List<Todos> todos = new List<Todos>(); protected override async Task OnInitializedAsync() { var request = new HttpRequestMessage(HttpMethod.Get, "https://jsonplaceholder.typicode.com/todos"); var httpClient = _httpClientFicatory.CreateClient(); var response = await httpClient.SendAsync(request); todos = await response.Content.ReadFromJsonAsync<List<Todos>>(); } }
- (Line: 3) IHttpClientFactory injected into the Blazor page
- (Line: 18) Initialized HttpRequestMessage object with action type and endpoint as the input parameters.
- (Line: 19)Using IHttpClienFactory creating the instance of the HttpClient
- (Line: 10)HttpRequestMessage is used as input parameter to the HttpClient object 'SendAsync' method.
Support Me!
Buy Me A Coffee
PayPal Me
Wrapping Up:
Hopefully, I think this article delivered some useful information on Blazor WebAssembly to call API endpoints using different techniques with HttpClient. I love to have your feedback, suggestions, and better techniques in the comment section below.
what is IHttpClientFactory ?
ReplyDeleteIt's create the appropriate HttpClient instance that registered in startup.cs
ReplyDeleteMicrosoft recomends to use it
Excellent article, thank you very much.
ReplyDeleteThanks, this is really neat, but in your example it will fail for CORS (has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource), how do you make jsonplaceholder.typicode.com work with CORS?
ReplyDeleteHi thanks, but it is free third party API, i didn't get any cors issue while using.
DeleteIf it throws cors now , we can't do anything it should be allowed by the third party API
Ah ok, thanks, it must be the third party then that doesn't like my browser.
DeleteGreat article it's very easy to follow and explains each type very well. Thank you for writing this.
ReplyDeleteHow do you call web api in blazor server .net core 6, while getting the url from the app settings.json as a configuration instead of hard coding it into Programs.cs,
ReplyDelete