Calling External APIs from ASP.NET Core Print

  • 0

HTTP Client Best Practices

Properly call external APIs from your ASP.NET Core application on Plesk.

Using IHttpClientFactory (Recommended)

// Program.cs - Register named client
builder.Services.AddHttpClient("GitHub", client =>
{
    client.BaseAddress = new Uri("https://api.github.com/");
    client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    client.DefaultRequestHeaders.Add("User-Agent", "MyApp");
});

// Or typed client
builder.Services.AddHttpClient<GitHubService>(client =>
{
    client.BaseAddress = new Uri("https://api.github.com/");
});

Using Named Client

public class MyService
{
    private readonly IHttpClientFactory _clientFactory;

    public MyService(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task<GitHubUser?> GetUserAsync(string username)
    {
        var client = _clientFactory.CreateClient("GitHub");

        var response = await client.GetAsync($"users/{username}");

        if (response.IsSuccessStatusCode)
        {
            return await response.Content.ReadFromJsonAsync<GitHubUser>();
        }

        return null;
    }
}

Typed Client Pattern

public class GitHubService
{
    private readonly HttpClient _client;

    public GitHubService(HttpClient client)
    {
        _client = client;
    }

    public async Task<IEnumerable<Repository>?> GetRepositoriesAsync(string user)
    {
        return await _client.GetFromJsonAsync<IEnumerable<Repository>>(
            $"users/{user}/repos");
    }
}

// Register
builder.Services.AddHttpClient<GitHubService>(client =>
{
    client.BaseAddress = new Uri("https://api.github.com/");
    client.DefaultRequestHeaders.UserAgent.ParseAdd("MyApp/1.0");
});

POST Requests with JSON

public async Task<Order?> CreateOrderAsync(CreateOrderDto dto)
{
    var response = await _client.PostAsJsonAsync("orders", dto);

    response.EnsureSuccessStatusCode();

    return await response.Content.ReadFromJsonAsync<Order>();
}

Adding Resilience with Polly

// Install: dotnet add package Microsoft.Extensions.Http.Polly

builder.Services.AddHttpClient<MyApiService>()
    .AddTransientHttpErrorPolicy(policy =>
        policy.WaitAndRetryAsync(3, retryAttempt =>
            TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))))
    .AddTransientHttpErrorPolicy(policy =>
        policy.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

Authentication Headers

// API Key
builder.Services.AddHttpClient("PaymentApi", client =>
{
    client.BaseAddress = new Uri("https://api.payment.com/");
    client.DefaultRequestHeaders.Add("X-Api-Key", "your-api-key");
});

// Bearer Token
builder.Services.AddHttpClient("SecureApi", client =>
{
    client.BaseAddress = new Uri("https://api.example.com/");
})
.AddHttpMessageHandler<AuthTokenHandler>();

// Token handler
public class AuthTokenHandler : DelegatingHandler
{
    private readonly ITokenService _tokenService;

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = await _tokenService.GetTokenAsync();
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        return await base.SendAsync(request, cancellationToken);
    }
}

Error Handling

public async Task<Result<User>> GetUserAsync(int id)
{
    try
    {
        var response = await _client.GetAsync($"users/{id}");

        if (response.IsSuccessStatusCode)
        {
            var user = await response.Content.ReadFromJsonAsync<User>();
            return Result<User>.Success(user!);
        }

        return response.StatusCode switch
        {
            HttpStatusCode.NotFound => Result<User>.Failure("User not found"),
            HttpStatusCode.Unauthorized => Result<User>.Failure("Unauthorized"),
            _ => Result<User>.Failure($"Error: {response.StatusCode}")
        };
    }
    catch (HttpRequestException ex)
    {
        return Result<User>.Failure($"Network error: {ex.Message}");
    }
}

Best Practices

  • Always use IHttpClientFactory, never new HttpClient()
  • Configure timeouts appropriately
  • Implement retry policies for transient failures
  • Use circuit breakers for failing services
  • Log HTTP requests in development
  • Handle all error responses gracefully

Was this answer helpful?

« Back