{
"success": false,
"errors": [
"Customer not found",
"Additional error message if applicable"
],
"correlationId": "0HN7QJKV3QJ8K:00000001",
"timestamp": "2025-10-21T14:30:00.000Z"
}
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.4",
"title": "Resource Not Found",
"status": 404,
"detail": "Customer with ID '12345' was not found.",
"correlationId": "0HN7QJKV3QJ8K:00000001",
"timestamp": "2025-10-21T14:30:00.000Z",
"service": "InternalApi"
}
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "Validation Failed",
"status": 400,
"detail": "One or more validation errors occurred.",
"correlationId": "0HN7QJKV3QJ8K:00000001",
"errors": {
"email": ["Email address is required"],
"firstName": ["First name must not exceed 50 characters"],
"quoteItems": ["At least one item is required"]
}
}
{
"type": "https://tools.ietf.org/html/rfc7235#section-3.1",
"title": "Unauthorized",
"status": 401,
"detail": "Authentication is required to access this resource.",
"correlationId": "0HN7QJKV3QJ8K:00000001"
}
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.3",
"title": "Forbidden",
"status": 403,
"detail": "You do not have permission to access this resource.",
"correlationId": "0HN7QJKV3QJ8K:00000001"
}
{
"error": "Request body does not match the original request for this idempotency key."
}
var response = await httpClient.PostAsync("/api/orders", content);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<Order>>();
if (!result.Success)
{
foreach (var error in result.Errors)
{
Console.WriteLine($"Error: {error}");
}
return;
}
// Process result.Data
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadFromJsonAsync<ProblemDetails>();
_logger.LogError(
"API error: {Title}. CorrelationId: {CorrelationId}",
error.Title,
error.CorrelationId);
}
if (response.StatusCode == HttpStatusCode.BadRequest)
{
var validation = await response.Content.ReadFromJsonAsync<ValidationProblemDetails>();
foreach (var (field, messages) in validation.Errors)
{
foreach (var message in messages)
{
Console.WriteLine($"{field}: {message}");
}
}
}
public async Task<T> ExecuteWithRetry<T>(Func<Task<HttpResponseMessage>> action, int maxRetries = 3)
{
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
var response = await action();
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadFromJsonAsync<T>();
}
// Don't retry client errors (4xx) - they won't succeed without changes
if ((int)response.StatusCode >= 400 && (int)response.StatusCode < 500)
{
throw new ApiException(await response.Content.ReadAsStringAsync());
}
// Retry server errors (5xx) with exponential backoff
if (attempt < maxRetries)
{
var delay = TimeSpan.FromSeconds(Math.Pow(2, attempt));
await Task.Delay(delay);
}
}
throw new MaxRetriesExceededException();
}