API Interaction

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:

  • Why do you need to register the HttpClient?
  • Register HttpClient for a single API.
  • Register HttpClient for multiple APIs.
  • Interfere HttpClient.
  • Send request and process response.
You can download the example code used in this topic on GitHub.

Why do you need to register the 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.


Register HttpClient for a single API

When 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

Register HttpClient for multiple APIs

When 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 class

A derived class is a class that extends the HttpClient class. You can also override methods of the HttpClient with this technique.

  1. Create a new class that extends the HttpClient class, set the BaseAddress property in the constructor. For example:
public class CustomHttpClient : HttpClient
{
    public CustomHttpClient()
    {
        BaseAddress = new("http://localhost:5165");
    }
}
  1. Register the class as a scoped service in 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

Named 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 wrapper

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

  1. Create a wrapper class with injected HttpClient. For example:
public class SecondApiHttpClientWrapper
{
    private readonly HttpClient _httpClient;

    public SecondApiHttpClientWrapper(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
}
  1. Register the wrapper class at 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

Interfere 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 middleware

This 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:

httpclient-middleware-chain.png

  1. Create a middleware class. The middleware class must extend the class 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 the base.Send() or base.SendAsync() when you are overriding the method Send or SendAsync respectively. If you don't, the chain is broken and the next middleware will not be called.
  1. Register all the middlewares are transient services at Program.cs. For example:
builder.Services.AddTransient<FirstMiddleware>();
builder.Services.AddTransient<SecondMiddleware>();
  1. Register the 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 wrapper

This technique provides you a centralized interfering code.

  1. Create the wrapper class. For example:
public class InterfereByHttpClientWrapper
{
    private readonly HttpClient _httpClient;

    public InterfereByHttpClientWrapper(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
}
  1. Expose a method to send the request. For example:
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 extension

This 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;
    }
}

Send request and process response

There are many HTTP methods, you can find out more at https://www.w3schools.com/tags/ref_httpmethods.asp.

Sending data via URL

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

Sending primitive data in the request body

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

Sending complex data in the request body

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.

Sending stream data in the request body

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.

Process primitive data in the response

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";
}

Automatically deserialize response data to object using Microsoft library

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

Deserialize response data to object using Newtonsoft library

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.

Process stream data in the response

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.
BLAZOR SCHOOL
Designed and built with care by our dedicated team, with contributions from a supportive community. We strive to provide the best learning experience for our users.
Docs licensed CC-BY-SA-4.0
Copyright © 2021-2025 Blazor School
An unhandled error has occurred. Reload 🗙