HttpClient – Tổng quan & sử dụng trong Blazor
Trang này trình bày quy trình làm việc với HttpClient trong Blazor theo 3 phần lớn:
IHttpClientFactory, HttpClient và AddHttpClient.
Mỗi phần đều có giải thích và ví dụ đầy đủ để dễ học và dễ đối chiếu khi làm project.
1️⃣ IHttpClientFactory
IHttpClientFactory là service dùng để tạo ra HttpClient.
Thay vì tự viết new HttpClient(), ta thường để factory này tạo client giúp.
Interface này chỉ có 1 method chính:
CreateClient(string name).
Trong quá trình dùng thực tế, ta thường gặp 2 kiểu gọi:
CreateClient()→ lấy client mặc địnhCreateClient("name")→ lấy named client theo cấu hình đã đăng ký
Điểm quan trọng là: Factory tạo client, còn HttpClient mới là đối tượng dùng để gửi request.
Xem code interface và cách dùng
public interface IHttpClientFactory
{
HttpClient CreateClient(string name);
}
@inject IHttpClientFactory Factory
🔹 1.1 CreateClient()
CreateClient() được dùng khi bạn muốn lấy client mặc định.
Khi dùng kiểu này, nếu trước đó bạn chỉ đăng ký:
builder.Services.AddHttpClient();
thì client tạo ra sẽ chưa có BaseAddress, header hay timeout riêng.
Vì vậy sau khi gọi CreateClient(), bạn thường phải tự set
BaseAddress, DefaultRequestHeaders hoặc dùng full URL.
Xem ví dụ đầy đủ với CreateClient()
builder.Services.AddHttpClient();
@inject IHttpClientFactory Factory
<button class="btn btn-primary" @onclick="LoadProducts">
Load Products
</button>
@if (_products is not null)
{
<ul class="mt-3">
@foreach (var item in _products)
{
<li>@item.Name - @item.Price</li>
}
</ul>
}
@code {
private List<Product>? _products;
private async Task LoadProducts()
{
var client = Factory.CreateClient();
client.BaseAddress = new Uri("https://localhost:7235/");
client.Timeout = TimeSpan.FromSeconds(30);
var response = await client.GetAsync("api/product/getproducts");
if (response.IsSuccessStatusCode)
{
_products = await response.Content.ReadFromJsonAsync<List<Product>>();
}
}
public class Product
{
public int Id { get; set; }
public string? Name { get; set; }
public double Price { get; set; }
public string? Description { get; set; }
}
}
🔹 1.2 CreateClient("name")
CreateClient("name") được dùng để lấy named client.
Tên truyền vào đây phải khớp với tên đã đăng ký trong Program.cs, ví dụ:
builder.Services.AddHttpClient("ProductApi", ...).
Khác với CreateClient(), kiểu này thường đi kèm với cấu hình riêng cho từng name.
Ví dụ:
"ProductApi"→ BaseAddress của Product API"AuthApi"→ BaseAddress của Auth API"OrderApi"→ timeout hoặc header riêng
Vì vậy, CreateClient("name") thường phù hợp hơn khi project có nhiều API hoặc cần cấu hình tách biệt.
Xem ví dụ đầy đủ với CreateClient("name")
builder.Services.AddHttpClient("ProductApi", client =>
{
client.BaseAddress = new Uri("https://localhost:7235/");
client.Timeout = TimeSpan.FromSeconds(30);
});
@inject IHttpClientFactory Factory
<button class="btn btn-primary" @onclick="LoadProducts">
Load Products By Named Client
</button>
@if (_products is not null)
{
<ul class="mt-3">
@foreach (var item in _products)
{
<li>@item.Name - @item.Price</li>
}
</ul>
}
@code {
private List<Product>? _products;
private async Task LoadProducts()
{
var client = Factory.CreateClient("ProductApi");
var response = await client.GetAsync("api/product/getproducts");
if (response.IsSuccessStatusCode)
{
_products = await response.Content.ReadFromJsonAsync<List<Product>>();
}
}
public class Product
{
public int Id { get; set; }
public string? Name { get; set; }
public double Price { get; set; }
public string? Description { get; set; }
}
}
2️⃣ HttpClient
HttpClient là class dùng để gửi request HTTP đến API và nhận response trả về.
Khi làm việc với HttpClient, bạn thường quan tâm đến 3 nhóm chính:
- Thuộc tính cấu hình
- Các method gửi request
- Cách đọc response
🔹 2.1 Các thuộc tính quan trọng
BaseAddress
Là URL gốc của API. Khi có BaseAddress, bạn không cần viết full URL trong mỗi lần gọi.
client.BaseAddress = new Uri("https://localhost:7235/");
Timeout
Là thời gian chờ tối đa cho request. Nếu server phản hồi quá lâu, request sẽ bị timeout.
client.Timeout = TimeSpan.FromSeconds(30);
DefaultRequestHeaders
Dùng để thêm header mặc định cho mọi request, ví dụ token, Accept, Authorization...
client.DefaultRequestHeaders.Add("token", "valid-token");
Xem ví dụ đầy đủ về thuộc tính
@inject IHttpClientFactory Factory
@code {
private async Task ConfigureClient()
{
var client = Factory.CreateClient();
client.BaseAddress = new Uri("https://localhost:7235/");
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Add("token", "valid-token");
var response = await client.GetAsync("api/product/getproducts");
}
}
🔹 2.2 Các method quan trọng
GetAsync → gửi GET request
var response = await client.GetAsync("api/product/getproducts");
PostAsync → gửi POST request với content tự tạo
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("api/product/addproduct", content);
PostAsJsonAsync → gửi object dưới dạng JSON
var response = await client.PostAsJsonAsync("api/product/addproduct", product);
PutAsJsonAsync → gửi PUT request với object JSON
var response = await client.PutAsJsonAsync("api/product/updateproduct/1", product);
DeleteAsync → gửi DELETE request
var response = await client.DeleteAsync("api/product/deleteproduct/1");
SendAsync → dùng khi cần tự tạo HttpRequestMessage để kiểm soát full request
var request = new HttpRequestMessage(HttpMethod.Get, "api/product/getproduct-token");
request.Headers.Add("token", "valid-token");
var response = await client.SendAsync(request);
Xem ví dụ đầy đủ về các method
@inject IHttpClientFactory Factory
@code {
private async Task RunExamples()
{
var client = Factory.CreateClient();
client.BaseAddress = new Uri("https://localhost:7235/");
var getResponse = await client.GetAsync("api/product/getproducts");
var product = new Product
{
Id = 10,
Name = "Product X",
Price = 100,
Description = "Created from HttpClient"
};
var postResponse = await client.PostAsJsonAsync(
"api/product/addproduct",
product
);
var putResponse = await client.PutAsJsonAsync(
"api/product/updateproduct/10",
product
);
var deleteResponse = await client.DeleteAsync(
"api/product/deleteproduct/10"
);
var request = new HttpRequestMessage(
HttpMethod.Get,
"api/product/getproduct-token"
);
request.Headers.Add("token", "valid-token");
var sendResponse = await client.SendAsync(request);
}
public class Product
{
public int Id { get; set; }
public string? Name { get; set; }
public double Price { get; set; }
public string? Description { get; set; }
}
}
🔹 2.3 Cách đọc response
IsSuccessStatusCode
Kiểm tra response có thành công hay không.
if (response.IsSuccessStatusCode)
{
// success
}
StatusCode
Lấy mã trạng thái HTTP, ví dụ 200, 404, 401.
var status = response.StatusCode;
ReadAsStringAsync()
Đọc response body dưới dạng text.
var text = await response.Content.ReadAsStringAsync();
ReadFromJsonAsync<T>()
Đọc JSON từ response body và map sang object C#.
var data = await response.Content.ReadFromJsonAsync<List<Product>>();
Xem ví dụ đầy đủ về đọc response
@inject IHttpClientFactory Factory
@code {
private async Task ReadResponseExample()
{
var client = Factory.CreateClient();
client.BaseAddress = new Uri("https://localhost:7235/");
var response = await client.GetAsync("api/product/getproducts");
if (response.IsSuccessStatusCode)
{
var products = await response.Content.ReadFromJsonAsync<List<Product>>();
}
else
{
var status = response.StatusCode;
var errorText = await response.Content.ReadAsStringAsync();
}
}
public class Product
{
public int Id { get; set; }
public string? Name { get; set; }
public double Price { get; set; }
public string? Description { get; set; }
}
}
3️⃣ AddHttpClient
AddHttpClient dùng để đăng ký và cấu hình cách tạo HttpClient trong DI.
Nói cách khác:
IHttpClientFactory→ là nơi tạo clientHttpClient→ là đối tượng dùng để gửi requestAddHttpClient→ là nơi cấu hình việc tạo client đó
Dưới đây là các kiểu đăng ký thường gặp.
🔹 3.1 AddHttpClient()
Đây là cách đăng ký cơ bản nhất.
Sau khi đăng ký kiểu này, bạn sẽ dùng:
Factory.CreateClient()
và tự cấu hình thêm khi dùng.
Phù hợp với demo hoặc project nhỏ.
Xem code ví dụ đầy đủ
builder.Services.AddHttpClient();
@inject IHttpClientFactory Factory
@code {
private async Task Example()
{
var client = Factory.CreateClient();
client.BaseAddress = new Uri("https://localhost:7235/");
client.Timeout = TimeSpan.FromSeconds(30);
var response = await client.GetAsync("api/product/getproducts");
}
}
🔹 3.2 AddHttpClient("name")
Đây là named client nhưng chưa có cấu hình sẵn.
Bạn sẽ dùng:
Factory.CreateClient("name")
rồi tự set BaseAddress nếu cần.
Cách này chủ yếu để hiểu cơ chế đặt tên.
Xem code ví dụ đầy đủ
builder.Services.AddHttpClient("ProductApi");
@inject IHttpClientFactory Factory
@code {
private async Task Example()
{
var client = Factory.CreateClient("ProductApi");
client.BaseAddress = new Uri("https://localhost:7235/");
var response = await client.GetAsync("api/product/getproducts");
}
}
🔹 3.3 AddHttpClient("name", client => { ... })
Đây là named client có cấu hình sẵn. Đây là một trong những cách dùng phổ biến nhất.
Bạn cấu hình ngay tại Program.cs, sau đó chỉ cần
CreateClient("name") rồi gọi API.
Phù hợp khi cần nhiều API với cấu hình khác nhau.
Xem code ví dụ đầy đủ
builder.Services.AddHttpClient("ProductApi", client =>
{
client.BaseAddress = new Uri("https://localhost:7235/");
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Add("token", "valid-token");
});
@inject IHttpClientFactory Factory
@code {
private async Task Example()
{
var client = Factory.CreateClient("ProductApi");
var response = await client.GetAsync("api/product/getproducts");
}
}
🔹 3.4 AddHttpClient<TClient>()
Đây là Typed Client. Bạn đưa logic gọi API vào một class riêng.
Với overload này, bạn vẫn phải tự set BaseAddress trong service nếu cần.
Cách này giúp UI gọn hơn, vì component chỉ inject service.
Xem code ví dụ đầy đủ
builder.Services.AddHttpClient<ProductApiService>();
using System.Net.Http.Json;
public class ProductApiService
{
private readonly HttpClient _http;
public ProductApiService(HttpClient http)
{
_http = http;
_http.BaseAddress = new Uri("https://localhost:7235/");
}
public async Task<List<Product>?> GetProducts()
{
return await _http.GetFromJsonAsync<List<Product>>(
"api/product/getproducts");
}
}
public class Product
{
public int Id { get; set; }
public string? Name { get; set; }
public double Price { get; set; }
public string? Description { get; set; }
}
@inject ProductApiService ProductService
@code {
private async Task Example()
{
var data = await ProductService.GetProducts();
}
}
🔹 3.5 AddHttpClient<TClient>(client => { ... })
Đây vẫn là Typed Client, nhưng cấu hình được đưa lên Program.cs.
Service sẽ sạch hơn vì không phải tự set BaseAddress trong constructor nữa.
Xem code ví dụ đầy đủ
builder.Services.AddHttpClient<ProductApiService>(client =>
{
client.BaseAddress = new Uri("https://localhost:7235/");
client.Timeout = TimeSpan.FromSeconds(30);
});
using System.Net.Http.Json;
public class ProductApiService
{
private readonly HttpClient _http;
public ProductApiService(HttpClient http)
{
_http = http;
}
public async Task<List<Product>?> GetProducts()
{
return await _http.GetFromJsonAsync<List<Product>>(
"api/product/getproducts");
}
}
@inject ProductApiService ProductService
@code {
private async Task Example()
{
var data = await ProductService.GetProducts();
}
}
🔹 3.6 AddHttpClient<TClient, TImplementation>()
Đây là cách đăng ký qua interface + implementation.
Với overload này, bạn inject bằng interface, còn implementation thật là class phía sau.
Phù hợp với clean architecture và unit test.
Xem code ví dụ đầy đủ
builder.Services.AddHttpClient<IProductService, ProductService>();
public interface IProductService
{
Task<List<Product>?> GetProducts();
}
using System.Net.Http.Json;
public class ProductService : IProductService
{
private readonly HttpClient _http;
public ProductService(HttpClient http)
{
_http = http;
_http.BaseAddress = new Uri("https://localhost:7235/");
}
public async Task<List<Product>?> GetProducts()
{
return await _http.GetFromJsonAsync<List<Product>>(
"api/product/getproducts");
}
}
public class Product
{
public int Id { get; set; }
public string? Name { get; set; }
public double Price { get; set; }
public string? Description { get; set; }
}
@inject IProductService ProductService
@code {
private async Task Example()
{
var data = await ProductService.GetProducts();
}
}
🔹 3.7 AddHttpClient<TClient, TImplementation>(client => { ... })
Đây là bản đầy đủ hơn của interface + implementation: vừa inject qua interface, vừa cấu hình sẵn HttpClient.
Đây là một trong những cách sạch nhất khi làm project thật.
Xem code ví dụ đầy đủ
builder.Services.AddHttpClient<IProductService, ProductService>(client =>
{
client.BaseAddress = new Uri("https://localhost:7235/");
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Add("token", "valid-token");
});
public interface IProductService
{
Task<List<Product>?> GetProducts();
}
using System.Net.Http.Json;
public class ProductService : IProductService
{
private readonly HttpClient _http;
public ProductService(HttpClient http)
{
_http = http;
}
public async Task<List<Product>?> GetProducts()
{
return await _http.GetFromJsonAsync<List<Product>>(
"api/product/getproducts");
}
}
public class Product
{
public int Id { get; set; }
public string? Name { get; set; }
public double Price { get; set; }
public string? Description { get; set; }
}
@inject IProductService ProductService
@code {
private async Task Example()
{
var data = await ProductService.GetProducts();
}
}
4️⃣ Tổng kết
- IHttpClientFactory → tạo
HttpClient - HttpClient → gửi request và đọc response
- AddHttpClient → cấu hình cách tạo client
- CreateClient() → lấy client mặc định
- CreateClient("name") → lấy named client theo cấu hình đã đăng ký