Yes, you can use HttpClient
in Blazor applications, and it's a core component for making HTTP requests in both Blazor hosting models. Blazor provides excellent support for HTTP operations through dependency injection and async patterns.
Blazor Hosting Models
Blazor offers two hosting models, each with different HttpClient considerations:
- Blazor WebAssembly: Runs in the browser using WebAssembly, with HTTP requests executed client-side
- Blazor Server: Runs on the server with UI updates sent over SignalR connections
Setup and Configuration
Blazor WebAssembly Setup
In Program.cs (NET 6+):
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
// Register HttpClient with base address
builder.Services.AddScoped(sp => new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
// Or use HttpClientFactory (recommended)
builder.Services.AddHttpClient("API", client =>
{
client.BaseAddress = new Uri("https://api.example.com/");
client.DefaultRequestHeaders.Add("User-Agent", "BlazorApp/1.0");
});
await builder.Build().RunAsync();
Blazor Server Setup
In Program.cs (NET 6+) or Startup.cs:
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
// Register HttpClient using factory pattern (recommended)
builder.Services.AddHttpClient();
// Named HttpClient for specific APIs
builder.Services.AddHttpClient("WeatherAPI", client =>
{
client.BaseAddress = new Uri("https://api.weather.com/");
client.Timeout = TimeSpan.FromSeconds(30);
});
var app = builder.Build();
Using HttpClient in Components
Basic Injection and Usage
@page "/weather"
@inject HttpClient Http
@inject IHttpClientFactory HttpClientFactory
<h3>Weather Data</h3>
@if (weather == null)
{
<p>Loading...</p>
}
else
{
<ul>
@foreach (var forecast in weather)
{
<li>@forecast.Date.ToShortDateString(): @forecast.Summary (@forecast.TemperatureC°C)</li>
}
</ul>
}
@code {
private WeatherForecast[]? weather;
protected override async Task OnInitializedAsync()
{
try
{
// Using injected HttpClient
weather = await Http.GetFromJsonAsync<WeatherForecast[]>("api/weather");
}
catch (HttpRequestException ex)
{
// Handle HTTP errors
Console.WriteLine($"Error fetching weather: {ex.Message}");
}
}
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
}
Using Named HttpClient with Factory
@page "/api-data"
@inject IHttpClientFactory HttpClientFactory
@code {
private ApiResponse? data;
protected override async Task OnInitializedAsync()
{
var client = HttpClientFactory.CreateClient("API");
try
{
data = await client.GetFromJsonAsync<ApiResponse>("data");
}
catch (Exception ex)
{
// Handle errors
}
}
}
HTTP Operations Examples
GET Requests
@code {
// Simple GET with string response
private async Task<string> GetStringData()
{
return await Http.GetStringAsync("api/text");
}
// GET with JSON deserialization
private async Task<List<Product>> GetProducts()
{
return await Http.GetFromJsonAsync<List<Product>>("api/products") ?? new();
}
// GET with custom headers
private async Task<ApiResponse> GetWithHeaders()
{
var request = new HttpRequestMessage(HttpMethod.Get, "api/secure-data");
request.Headers.Add("Authorization", "Bearer " + token);
var response = await Http.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<ApiResponse>();
}
}
POST Requests
@code {
// POST with JSON content
private async Task<bool> CreateProduct(Product product)
{
try
{
var response = await Http.PostAsJsonAsync("api/products", product);
return response.IsSuccessStatusCode;
}
catch (HttpRequestException)
{
return false;
}
}
// POST with form data
private async Task<bool> SubmitForm(Dictionary<string, string> formData)
{
var formContent = new FormUrlEncodedContent(formData);
var response = await Http.PostAsync("api/form", formContent);
return response.IsSuccessStatusCode;
}
// POST with file upload
private async Task<bool> UploadFile(IBrowserFile file)
{
using var content = new MultipartFormDataContent();
using var fileContent = new StreamContent(file.OpenReadStream());
content.Add(fileContent, "file", file.Name);
var response = await Http.PostAsync("api/upload", content);
return response.IsSuccessStatusCode;
}
}
PUT and DELETE Requests
@code {
// PUT request
private async Task<bool> UpdateProduct(int id, Product product)
{
var response = await Http.PutAsJsonAsync($"api/products/{id}", product);
return response.IsSuccessStatusCode;
}
// DELETE request
private async Task<bool> DeleteProduct(int id)
{
var response = await Http.DeleteAsync($"api/products/{id}");
return response.IsSuccessStatusCode;
}
}
Error Handling and Best Practices
Comprehensive Error Handling
@code {
private async Task<T?> SafeHttpCall<T>(Func<Task<T>> httpCall) where T : class
{
try
{
return await httpCall();
}
catch (HttpRequestException ex)
{
Console.WriteLine($"HTTP Error: {ex.Message}");
return null;
}
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
{
Console.WriteLine("Request timed out");
return null;
}
catch (JsonException ex)
{
Console.WriteLine($"JSON parsing error: {ex.Message}");
return null;
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected error: {ex.Message}");
return null;
}
}
// Usage
private async Task LoadData()
{
var data = await SafeHttpCall(async () =>
await Http.GetFromJsonAsync<ApiResponse>("api/data"));
if (data != null)
{
// Process data
}
}
}
Response Status Checking
@code {
private async Task<(bool Success, T? Data, string? Error)> GetWithStatusCheck<T>(string endpoint)
{
var response = await Http.GetAsync(endpoint);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadFromJsonAsync<T>();
return (true, data, null);
}
var error = response.StatusCode switch
{
HttpStatusCode.NotFound => "Resource not found",
HttpStatusCode.Unauthorized => "Unauthorized access",
HttpStatusCode.Forbidden => "Access forbidden",
HttpStatusCode.InternalServerError => "Server error occurred",
_ => $"Request failed with status: {response.StatusCode}"
};
return (false, default(T), error);
}
}
Blazor-Specific Considerations
CORS in Blazor WebAssembly
// When calling external APIs, ensure CORS is configured on the server
@code {
protected override async Task OnInitializedAsync()
{
try
{
// This may fail due to CORS if the API doesn't allow your domain
var data = await Http.GetFromJsonAsync<ApiData>("https://external-api.com/data");
}
catch (HttpRequestException ex)
{
// Could be a CORS issue
Console.WriteLine($"Request failed - check CORS configuration: {ex.Message}");
}
}
}
Authentication with HttpClient
@page "/secure-data"
@inject HttpClient Http
@inject IJSRuntime JSRuntime
@code {
protected override async Task OnInitializedAsync()
{
// Get token from localStorage or cookie
var token = await JSRuntime.InvokeAsync<string>("localStorage.getItem", "authToken");
if (!string.IsNullOrEmpty(token))
{
Http.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
}
var secureData = await Http.GetFromJsonAsync<SecureData>("api/secure");
}
}
Performance Tips
- Use IHttpClientFactory: Prevents socket exhaustion and enables better configuration
- Dispose appropriately: HttpClient is typically managed by DI container
- Use streaming for large responses: Use
GetStreamAsync()
for large files - Configure timeouts: Set reasonable timeout values
- Cache responses: Implement caching for frequently accessed data
// Example of streaming large responses
@code {
private async Task DownloadLargeFile()
{
using var response = await Http.GetAsync("api/large-file", HttpCompletionOption.ResponseHeadersRead);
using var stream = await response.Content.ReadAsStreamAsync();
// Process stream in chunks
var buffer = new byte[8192];
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
// Process chunk
}
}
}
HttpClient is fully supported and essential for HTTP operations in Blazor applications. The key is proper configuration through dependency injection and following async/await patterns consistently.