API interaction is an important technique that you must know in Blazor WebAssembly. API interaction is about sending request and process response from the API server. In this tutorial, you will discover:
HttpClient
?HttpClient
for a single API.HttpClient
for multiple APIs.HttpClient
.You can download the example code used in this topic on GitHub.
HttpClient
?You will make http requests to API server by using HttpClient
. You need to register the HttpClient
first. Depend on how many APIs in your website, you will need to register them differently. The BaseAddress
in the HttpClient
is the address of your API and you must specify this value in order to make HttpClient
to work.
HttpClient
for a single APIWhen your website has only one API, you can add HttpClient
as a scoped service at Program.cs
. Assuming your API endpoint is http://localhost:5165, you can register as follows:
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new("http://localhost:5165") });
Then you can inject the HttpClient
to any razor component to make a request. For example:
@inject HttpClient HttpClient
HttpClient
for multiple APIsWhen your website has multiple APIs, you can register HttpClient
in many ways. Assuming your API endpoints are http://localhost:5165 and http://localhost:5276.
HttpClient
derived classA derived class is a class that extends the HttpClient
class. You can also override methods of the HttpClient
with this technique.
HttpClient
class, set the BaseAddress
property in the constructor. For example:public class CustomHttpClient : HttpClient { public CustomHttpClient() { BaseAddress = new("http://localhost:5165"); } }
Program.cs
. For example:builder.Services.AddScoped<CustomHttpClient>();
Whenever you need to make a request, you need to inject it as with the single API approach. For example:
@inject CustomHttpClient CustomHttpClient
HttpClient
You name your API differently, then use the IHttpClientFactory
and create the HttpClient
by its name. This technique does not allow you to override methods of the HttpClient
. Register your HttpClient
instances at Program.cs
. You must provide a name for each instance. For example:
builder.Services.AddHttpClient("First API", httpClient => httpClient.BaseAddress = new("http://localhost:5165")); builder.Services.AddHttpClient("Second API", httpClient => httpClient.BaseAddress = new("http://localhost:5276"));
Whenever you need to make a request, you need to inject the IHttpClientFactory
and create the HttpClient
by its name. For example:
@inject IHttpClientFactory HttpClientFactory ... @code { protected override async Task OnInitializedAsync() { var httpClient = HttpClientFactory.CreateClient("First API"); ... } }
HttpClient
wrapperA wrapper is a class that takes HttpClient
from the service provider, then hiding all the methods of HttpClient
. The wrapper class then expose its own methods for other classes.
HttpClient
. For example:public class SecondApiHttpClientWrapper { private readonly HttpClient _httpClient; public SecondApiHttpClientWrapper(HttpClient httpClient) { _httpClient = httpClient; } }
Program.cs
. For example:builder.Services.AddHttpClient<SecondApiHttpClientWrapper>(httpClient => httpClient.BaseAddress = new("http://localhost:5276"));
Whenever you need to make a request, you can inject the wrapper class and use the exposed method of the wrapper class to make the request. For example:
@inject SecondApiHttpClientWrapper SecondApiHttpClientWrapper
HttpClient
Some APIs are protected by JWT, some require additional request header, sometimes you need to interact with the loading indicator whenever a request goes and a response comes. This is why you need to interfere the HttpClient
. There are many ways to interfere the HttpClient
:
HttpClient
middleware.HttpClient
wrapper.HttpClient
extension.HttpClient
middlewareThis technique is build on the chain of responsibility pattern. You will have many middlewares, a request is processed by the first middleware in the chain to the last middleware in the chain, then a response is processed by the last middleware in the chain to the first middleware in the chain. The following image illustrates this pattern:
DelegatingHandler
. The class must override at least one method in Send
or SendAsync
. For example:public class FirstMiddleware : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // Interfering code before sending the request var response = await base.SendAsync(request, cancellationToken); // Interfering code after sending the request return response; } }
You must call thebase.Send()
orbase.SendAsync()
when you are overriding the methodSend
orSendAsync
respectively. If you don't, the chain is broken and the next middleware will not be called.
Program.cs
. For example:builder.Services.AddTransient<FirstMiddleware>(); builder.Services.AddTransient<SecondMiddleware>();
HttpClient
by named HttpClient
or HttpClient
wrapper technique, then add the chain in your desired sequence. For example:builder.Services.AddHttpClient("HttpClient with Middlewares", httpClient => httpClient.BaseAddress = new("http://localhost:5276")) .AddHttpMessageHandler<FirstMiddleware>() .AddHttpMessageHandler<SecondMiddleware>(); builder.Services.AddHttpClient<SecondApiHttpClientWrapper>(httpClient => httpClient.BaseAddress = new("http://localhost:5276")) .AddHttpMessageHandler<FirstMiddleware>() .AddHttpMessageHandler<SecondMiddleware>();
HttpClient
wrapperThis technique provides you a centralized interfering code.
public class InterfereByHttpClientWrapper { private readonly HttpClient _httpClient; public InterfereByHttpClientWrapper(HttpClient httpClient) { _httpClient = httpClient; } }
public class InterfereByHttpClientWrapper { ... public async Task<T?> GetAsync<T>(string requestUrl) { // Interfering code before sending the request var response = await _httpClient.GetFromJsonAsync<T>(requestUrl); // Interfering code after sending the request return response; } }
HttpClient
extensionThis technique provides you a centralized interfering code.
Create the extension class. Create a method to send request with the first parameter is this HttpClient httpClient
. For example:
public static class HttpClientExtension { public static async Task<T?> GetResponse<T>(this HttpClient httpClient, string url) { // Interfering code before sending the request var result = await httpClient.GetFromJsonAsync<T>(url); // Interfering code after sending the request return result; } }
There are many HTTP methods, you can find out more at https://www.w3schools.com/tags/ref_httpmethods.asp.
You can put some data in the URL and send it to the API. This commonly used in API with GET method. The data in the URL should be primitive data type and short. For a big data, do not send it via URL. You can use HttpClient
to send data via URL as follows:
await httpClient.GetAsync($"example/ProcessPrimitiveUrlData?data=Blazor School");
For HTTP methods other than GET, you need to put the data in the request body instead of the URL. You can send primitive data in the request body as follows:
await httpClient.PostAsync("example/ProcessPrimitiveData", new StringContent("\"Blazor School\"", Encoding.UTF8, "application/json"));
Similar to sending primitive data in the request body, this technique is used for HTTP methods other than GET. You need to serialize your object to JSON then send the JSON to the API. There are a few methods in the System.Net.Http.Json
that helps you to do that. For example:
var data = new ExampleClass() { ExampleString = "Blazor School" }; await httpClient.PostAsJsonAsync<ExampleClass>("example/ProcessComplexData", data);
The PostAsJsonAsync
will serialize your object to JSON and then send it to the API with POST method. You can also use the similar methods for other HTTP methods.
A stream is a sequence of bytes. The bytes can be a file content, a video, etc... Most case stream is used to upload a file to the API. Here is an example code to upload a file in Blazor WebAssembly:
using var formDataContent = new MultipartFormDataContent(); using var stream = ExampleFile.OpenReadStream(long.MaxValue); using var streamContent = new StreamContent(stream); streamContent.Headers.ContentType = new(ExampleFile.ContentType); formDataContent.Add(streamContent, "FileStream", ExampleFile.Name); var message = await httpClient.PostAsync("example/ProcessStreamdata", formDataContent);
In the above example, the ExampleFile
is the object of IBrowserFile
type. You can also add more files to the formDataContent
in the example.
After sending a request to the API, the API will process the request then return the response to your HttpClient
. Sometimes, the response has the data, sometimes it doesn't. When the response has data, you can process the data as follows:
var response = await httpClient.GetAsync("example/ReturnPrimitiveData"); if(response.IsSuccessStatusCode) { DataReceived = await response.Content.ReadAsStringAsync(); } else { DataReceived = "Failed"; }
When your API returns a JSON, you can use the Microsoft library System.Net.Http.Json
to automatically deserialize the JSON to a C# object. This library will use System.Text.Json
underlying to deserialize the JSON. Http For example:
@inject IHttpClientFactory HttpClientFactory <div>Data Received: @ExampleInstance.ExampleString</div> @code { public ExampleClass ExampleInstance { get; set; } = new(); protected override async Task OnInitializedAsync() { var httpClient = HttpClientFactory.CreateClient("Second API"); ExampleInstance = await httpClient.GetFromJsonAsync<ExampleClass>("example/ReturnComplexData") ?? new(); } }
Newtonsoft is a famous JSON process library with awesome features that System.Text.Json
doesn't have. You can also use the Newtonsoft to deserialize the JSON to a C# object as follows:
@using Newtonsoft.Json @inject IHttpClientFactory HttpClientFactory <div>Data Received: @ExampleInstance.ExampleString</div> @code { public ExampleClass ExampleInstance { get; set; } = new(); protected override async Task OnInitializedAsync() { var httpClient = HttpClientFactory.CreateClient("Second API"); var response = await httpClient.GetAsync("example/ReturnComplexData"); if(response.IsSuccessStatusCode) { string responseContent = await response.Content.ReadAsStringAsync(); ExampleInstance = JsonConvert.DeserializeObject<ExampleClass>(responseContent) ?? new(); } } }
You can improve this technique to not repeat the same code over again by using one of the technique in Interfere HttpClient section.
Some APIs will return a stream, you can also process the stream by the method ReadAsStreamAsync()
. The following example code will process an image file stream.
@inject IHttpClientFactory HttpClientFactory <img src="@ImageSrc"/> @code { public string ImageSrc { get; set; } = ""; protected override async Task OnInitializedAsync() { var httpClient = HttpClientFactory.CreateClient("Second API"); var response = await httpClient.GetAsync("example/ReturnStreamData"); if(response.IsSuccessStatusCode) { using var stream = await response.Content.ReadAsStreamAsync(); byte[] buffer = new byte[stream.Length]; await stream.ReadAsync(buffer, 0, (int)stream.Length); string base64 = Convert.ToBase64String(buffer); ImageSrc = $"data:image/png;base64,{base64}"; } StateHasChanged(); } }
You need to process the stream according to its type. It's not always an image file stream as in the example.