API is valuable for every website, it allows your website to talk to another product. In this tutorial, you will discover:
HttpClient
?HttpClient
.HttpClient
.You can download the example code used in this topic on GitHub.
HttpClient
?When integrating with a third party API, your server (website host) became the client of another server. You will make http requests from your server to the 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. The following image illustrates the website integration model.
HttpClient
In this tutorial, we will assume you have 2 API endpoints http://localhost:5132 (First API) and http://localhost:5016 (Second API).
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:5132")); builder.Services.AddHttpClient("Second API", httpClient => httpClient.BaseAddress = new("http://localhost:5016"));
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("Second 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 HttpClientWrapperClass { private readonly HttpClient _httpClient; public HttpClientWrapperClass(HttpClient httpClient) { _httpClient = httpClient; } }
public async Task<string> GetWeatherDataAsync() { var response = await _httpClient.GetAsync("/weatherforecast"); string result = await response.Content.ReadAsStringAsync(); return result; }
Program.cs
. For example:builder.Services.AddHttpClient<HttpClientWrapperClass>(httpClient => httpClient.BaseAddress = new("http://localhost:5132"));
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 HttpClientWrapperClass HttpClientWrapperClass
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 Middleware", 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, and you should follow the method to standardize your website. You can find out more at https://www.w3schools.com/tags/ref_httpmethods.asp.
Sending requests is a must-know technique when working with the API. In this section, we will get you through how to send a request to an API endpoint.
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 small. 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");
If you have then one parameter, you can use the symbol &
to separate them. For example: example/ProcessPrimitiveUrlData?data=Blazor School&book=Blazor Server .NET 6 Fundamentals.
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"));
This technique is only applicable if your API endpoint has only 1 parameter. If your API endpoint has more than 1 parameter, use the sending complex data technique.
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 from your Blazor Server host to the API endpoint:
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 your request is received at the API endpoint, the API will process your request and give you a response. This section will give you an idea on how to process the response of an API endpoint.
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.Json
is a famous JSON process library with awesome features that System.Text.Json
doesn't have. You can also use the Newtonsoft.Json
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.
When integrating with an API in Blazor Server, there are some common mistakes that you need to avoid. They are:
HttpClient
class.You should not build/use a first party API for Blazor Server. The reason is:
If you already have an API and want to reuse the logic in the API, you need to split the logic to a Class Library project and then use that Class Library project in your Blazor Server website.
HttpClient
classYou should not extend HttpClient
in Blazor Server (however in Blazor WebAssembly, you can use this technique). The method AddHttpClient
do not support a derived HttpClient
class and you have to manage the life cycle of your extended class manually.
HttpClient
's life cycle correctly?Your Blazor Server website host will suffer socket exhaustion.
Consider the following code:
@page "/extend-http-client" <h3>ExtendHttpClient</h3> <div>@((MarkupString)Log)</div> @code { public string Log { get; set; } = ""; protected override async Task OnInitializedAsync() { foreach (int index in Enumerable.Range(1, 10)) { using var httpClient = new HttpClient(); var response = await httpClient.GetAsync("https://blazorschool.com"); if(response.IsSuccessStatusCode) { Log += $"Request {index} was sucessfully.<br/>"; } else { Log += $"Request {index} was failed.<br/>"; } } } }
The following video will demonstrate what will happend with the code: