Integrating external APIs is a common task in modern web applications. In ASP.NET Core Razor Pages, HttpClient
is the preferred class for making HTTP requests. This post walks you through how to use HttpClient
in ASP.NET Core, avoid common pitfalls, and demonstrate how to integrate it effectively in a modern application.
Working with HttpClient in ASP.NET Core
Getting Started
When building modern applications, consuming external APIs is common. In ASP.NET Core, the recommended way to make HTTP requests is using the built-in HttpClient
.
HttpClient is a class provided by the .NET framework for sending HTTP requests and receiving responses from web APIs or web services. It supports:
- Sending asynchronous HTTP requests (
GET
,POST
,PUT
,DELETE
) - Handling headers, cookies, and authentication.
- Working with JSON and other content types.
Basic Usage of HttpClient
public class MyApiClient
{
private readonly HttpClient _httpClient;
public MyApiClient(HttpClient httpClient)
{
_httpClient = httpClient;
_httpClient.BaseAddress = new Uri("https://api.example.com/");
}
public async Task<string> GetDataAsync()
{
var response = await _httpClient.GetAsync("data");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
Note:- Avoid initiating to HttpClient
directly, A common mistake is to instantiate HttpClient
directly using new HttpClient
(). This can lead to socket exhaustion, especially under load, due to how DNS is handled internally by HttpClient
. Instead, user the IHttpClientFactory
which is provided by ASP.NET Core to create and manage HttpClient instances efficiently.
Steps to Intigrate HttpClient
Integrating HttpClient
in ASP.NET Core is a common task for calling external APIs or services. ASP.NET Core provides a few best-practice ways to do this using dependency injection and HttpClientFactory
, which avoids common pitfalls like socket exhaustion.
Here are the steps to integrate HttpClient
in ASP.NET Core:
Register HttpClient
in Startup.cs
or Program.cs
In .NET 6+ (Minimal Hosting Model):
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Register a basic HttpClient
builder.Services.AddHttpClient();
// OR named client
builder.Services.AddHttpClient("MyApiClient", client =>
{
client.BaseAddress = new Uri("https://api.example.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
});
In .NET Core 3.1 or .NET 5 (with Startup.cs):
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
}
Inject HttpClient
into your service or controller
HttpClient
can be inject in three ways as given below.Using Default
HttpClient
public class MyService
{
private readonly HttpClient _httpClient;
public MyService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<string> GetDataAsync()
{
var response = await _httpClient.GetAsync("https://api.example.com/data");
return await response.Content.ReadAsStringAsync();
}
}
Register the Service:
services.AddTransient<MyService>();
Using a Named Client
public class MyService
{
private readonly HttpClient _httpClient;
public MyService(IHttpClientFactory httpClientFactory)
{
_httpClient = httpClientFactory.CreateClient("MyApiClient");
}
public async Task<string> GetDataAsync()
{
var response = await _httpClient.GetAsync("endpoint");
return await response.Content.ReadAsStringAsync();
}
}
Using a Typed ClientThis is a best practice for APIs. Create a typed client class:
public class MyApiClient
{
private readonly HttpClient _httpClient;
public MyApiClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<string> GetDataAsync()
{
var response = await _httpClient.GetAsync("data");
return await response.Content.ReadAsStringAsync();
}
}
Register it like this:
builder.Services.AddHttpClient<MyApiClient>(client =>
{
client.BaseAddress = new Uri("https://api.example.com/");
});
Use it in your controller
public class HomeController : Controller
{
private readonly MyApiClient _apiClient;
public HomeController(MyApiClient apiClient)
{
_apiClient = apiClient;
}
public async Task<IActionResult> Index()
{
var data = await _apiClient.GetDataAsync();
return Content(data);
}
}
Handle Errors and Timeouts Gracefully
Add policies (like retry, circuit breaker) using Polly:builder.Services.AddHttpClient("MyApiClient")
.AddTransientHttpErrorPolicy(policy =>
policy.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(600)));
Testing
Here basic code to test the HttpClient
using HttpMessageHandler
mocking.
var handlerMock = new Mock<HttpMessageHandler>();
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("{'key':'value'}"),
});
var httpClient = new HttpClient(handlerMock.Object);
Summary
HttpClient
is a powerful tool in ASP.NET Core, but using it correctly is critical for performance, reliability, and testability. Always prefer HttpClientFactory
and consider typed clients for clean, testable code.
Thanks