blazor-httpclient-rest-api-factory

HttpClient in Blazor: Consuming APIs with Typed Clients

  • 3 min

HttpClient is the .NET API for sending HTTP requests and receiving responses from remote services.

In Interactive WebAssembly, the code runs inside the browser and needs an API to access server data. In SSR or Interactive Server, HttpClient also allows calling internal or external HTTP services.

On the server, we must properly manage the reuse of its connections. In WebAssembly, HttpClient relies on the browser’s fetch API.

The problem with new HttpClient()

Instinctively, we might think of doing this inside a service:

// ⛔ BAD: Don't do this in production
using (var client = new HttpClient())
{
    var response = await client.GetAsync("https://miapi.com/productos");
}
Copied!

In .NET server-side, creating and destroying clients per request prevents proper reuse of handlers and can lead to port exhaustion or DNS update issues.

To solve this, Microsoft introduced IHttpClientFactory.

This factory reuses and rotates HTTP handlers, and also allows configuring clients centrally. To use it in a Blazor client project, add the Microsoft.Extensions.Http package.

Configuring typed clients

A convenient option is to use typed clients, which encapsulate the calls to a specific API.

This binds a specific HTTP configuration (base URL, headers) to a concrete service class.

Registration in Program.cs

Instead of just using AddScoped, we use AddHttpClient.

// Program.cs

// Register the service and configure its associated HttpClient
builder.Services.AddHttpClient<IProductoService, ProductoServiceAPI>(client =>
{
    // Configure the base URL. This way the service doesn't need to know the server's IP.
    client.BaseAddress = new Uri("https://api.luisllamas.es/v1/");

    // We can add global headers (e.g., API Key, User-Agent)
    client.DefaultRequestHeaders.Add("Accept", "application/json");
});
Copied!

Injection into the service

Now, in our ProductoServiceAPI class, we inject HttpClient via the constructor. The received instance already contains the BaseAddress and the headers configured in Program.cs.

public class ProductoServiceAPI : IProductoService
{
    private readonly HttpClient _http;

    public ProductoServiceAPI(HttpClient http)
    {
        _http = http;
    }

    // Implementation methods...
}
Copied!

JSON Extensions (System.Net.Http.Json)

In the past, reading an object from an API was a three-step process:

  1. client.GetAsync(...)
  2. response.Content.ReadAsStringAsync()
  3. JsonSerializer.Deserialize<T>(json)

In modern .NET, we have extension methods that do all this in a single line and in an optimized way.

GET: Retrieve data

public async Task<List<ProductoDto>> GetProductosAsync()
{
    // Makes a GET to "https://api.luisllamas.es/v1/productos"
    // Automatically deserializes the JSON to List<ProductoDto>
    // If the response is not successful, throws HttpRequestException
    return await _http.GetFromJsonAsync<List<ProductoDto>>("productos")
           ?? new List<ProductoDto>();
}
Copied!

POST: Send data

public async Task CrearProductoAsync(ProductoDto nuevoProducto)
{
    // Serializes the object to JSON and sends it via POST
    var respuesta = await _http.PostAsJsonAsync("productos", nuevoProducto);

    if (!respuesta.IsSuccessStatusCode)
    {
        // Handle the error
        var error = await respuesta.Content.ReadAsStringAsync();
        throw new Exception($"Error creating: {error}");
    }
}
Copied!

The methods GetFromJsonAsync, PostAsJsonAsync, and PutAsJsonAsync are found in the System.Net.Http.Json namespace. Make sure you have the corresponding using directive.

Differences: Server vs WebAssembly

It’s important to understand that, although the C# code is identical, what happens “under the hood” is different:

  1. In Blazor Server: The HttpClient runs on the server. It can access other internal APIs on your network (microservices) without CORS issues.
  2. In Blazor WebAssembly: The HttpClient is just a wrapper around the browser’s fetch API.
    • You are limited by the browser’s security rules (Same-Origin Policy).
    • If your API is on a different domain, you must configure CORS (Cross-Origin Resource Sharing) on the API server to allow the requests.