ASP.NET Core MVC & Razor Pages Documentation
This guide dives into advanced techniques for integrating your ASP.NET Core MVC or Razor Pages applications with external APIs. We'll cover strategies for efficient data fetching, error handling, security, and asynchronous operations.
Leveraging the built-in HttpClient
class is crucial for non-blocking API requests. Always use asynchronous methods like GetAsync
, PostAsync
, etc., and ensure your controller actions or page models are also asynchronous (marked with async
and return Task<IActionResult>
or Task<PageResult>
).
using System.Net.Http;
using System.Threading.Tasks;
public class ApiService
{
private readonly HttpClient _httpClient;
public ApiService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<string> GetDataFromApiAsync(string url)
{
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode(); // Throws an exception if the status code is not success
return await response.Content.ReadAsStringAsync();
}
}
Registering HttpClient
with the dependency injection container is the recommended approach. This promotes proper management of HttpClient
instances, preventing socket exhaustion issues.
Recommendation: Use AddHttpClient
in your Startup.cs
or Program.cs
.
// In Program.cs (.NET 6+) or Startup.cs (.NET Core 3.1 and earlier)
builder.Services.AddHttpClient();
// Or to configure a named client:
builder.Services.AddHttpClient("MyApiClient", client =>
{
client.BaseAddress = new Uri("https://api.example.com/");
client.DefaultRequestHeaders.Add("Accept", "application/json");
});
Once you receive a response, you'll likely need to deserialize the JSON (or other format) into C# objects. The System.Text.Json
namespace provides efficient and modern JSON serialization.
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.Json;
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class ProductApiService
{
private readonly HttpClient _httpClient;
public ProductApiService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<Product> GetProductAsync(int productId)
{
var response = await _httpClient.GetAsync($"products/{productId}");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<Product>(json);
}
}
API integrations are prone to various errors: network issues, invalid responses, rate limiting, etc. Implement comprehensive error handling.
response.EnsureSuccessStatusCode()
to catch HTTP errors.try-catch
blocks to handle HttpRequestException
, JsonException
, and other potential exceptions.
public async Task<IActionResult> LoadProduct(int id)
{
try
{
var product = await _productApiService.GetProductAsync(id);
if (product == null)
{
return NotFound(); // Return a 404 Not Found
}
return Ok(product); // Return the product data
}
catch (HttpRequestException httpEx)
{
// Log the HTTP error details
_logger.LogError(httpEx, "HTTP request failed for product ID {ProductId}", id);
return StatusCode(503, "Service Unavailable"); // Example: return a 503 error
}
catch (JsonException jsonEx)
{
// Log JSON deserialization error
_logger.LogError(jsonEx, "Failed to deserialize API response for product ID {ProductId}", id);
return BadRequest("Invalid data received from API.");
}
catch (Exception ex)
{
// Catch any other unexpected errors
_logger.LogError(ex, "An unexpected error occurred while fetching product ID {ProductId}", id);
return StatusCode(500, "An internal error occurred.");
}
}
Many APIs impose rate limits to prevent abuse. Be aware of these limits and implement strategies to respect them:
Secure your API calls using appropriate authentication mechanisms:
Ensure your HttpClient
is configured with the necessary authentication headers.
// Example: Adding an API Key header
_httpClient.DefaultRequestHeaders.Add("X-API-Key", "YOUR_SECRET_API_KEY");
// Example: Adding an Authorization header for Bearer token
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "YOUR_ACCESS_TOKEN");
The Polly library is an excellent choice for implementing resilience and transient-fault-handling policies in .NET.
Install the Polly
NuGet package.
using Polly;
using Polly.Retry;
using System.Net.Http;
public class ResilientApiService
{
private readonly HttpClient _httpClient;
private readonly AsyncRetryPolicy _retryPolicy;
public ResilientApiService(HttpClient httpClient)
{
_httpClient = httpClient;
_retryPolicy = Policy
.Handle<HttpRequestException>()
.Or<TimeoutException>()
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
public async Task<string> GetDataWithRetryAsync(string url)
{
return await _retryPolicy.ExecuteAsync(async () =>
{
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
});
}
}
HttpClient
correctly and inject it via DI.Example Scenario: Fetching a list of users from a third-party API and displaying them in a Razor Page.
This involves creating a service to fetch data, handling potential nulls or errors, and binding the data to the page model.