littleshop/LittleShop.Client/Http/ErrorHandlingMiddleware.cs
sysadmin 1f7c0af497 Add LittleShop.Client SDK library with complete API wrapper
Features:
- Complete .NET client SDK for LittleShop API
- JWT authentication with automatic token management
- Catalog service for products and categories
- Order service with payment creation
- Retry policies using Polly for resilience
- Error handling middleware
- Dependency injection support
- Comprehensive documentation and examples

SDK Components:
- Authentication service with token refresh
- Strongly-typed models for all API responses
- HTTP handlers for retry and error handling
- Extension methods for easy DI registration
- Example console application demonstrating usage

Test Updates:
- Fixed test compilation errors
- Updated test data builders for new models
- Corrected service constructor dependencies
- Fixed enum value changes (PaymentStatus, OrderStatus)

Documentation:
- Complete project README with features and usage
- Client SDK README with detailed examples
- API endpoint documentation
- Security considerations
- Deployment guidelines

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-20 18:15:35 +01:00

99 lines
3.5 KiB
C#

using System.Net;
using System.Text.Json;
using Microsoft.Extensions.Logging;
namespace LittleShop.Client.Http;
public class ErrorHandlingMiddleware : DelegatingHandler
{
private readonly ILogger<ErrorHandlingMiddleware> _logger;
public ErrorHandlingMiddleware(ILogger<ErrorHandlingMiddleware> logger)
{
_logger = logger;
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
try
{
var response = await base.SendAsync(request, cancellationToken);
// Log errors but don't throw - let the service layer handle the response
if (!response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync(cancellationToken);
_logger.LogWarning(
"HTTP {StatusCode} for {Method} {Uri}: {Content}",
(int)response.StatusCode,
request.Method,
request.RequestUri,
content);
// Try to parse error response
if (!string.IsNullOrEmpty(content))
{
try
{
var errorResponse = JsonSerializer.Deserialize<ErrorResponse>(content,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (errorResponse != null && !string.IsNullOrEmpty(errorResponse.Message))
{
// Replace content with structured error message
response.Content = new StringContent(errorResponse.Message);
}
}
catch
{
// If we can't parse the error, leave original content
}
}
}
return response;
}
catch (TaskCanceledException ex)
{
_logger.LogError(ex, "Request timeout for {Method} {Uri}",
request.Method, request.RequestUri);
return new HttpResponseMessage(HttpStatusCode.RequestTimeout)
{
Content = new StringContent("Request timed out"),
RequestMessage = request
};
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Network error for {Method} {Uri}",
request.Method, request.RequestUri);
return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable)
{
Content = new StringContent($"Network error: {ex.Message}"),
RequestMessage = request
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error for {Method} {Uri}",
request.Method, request.RequestUri);
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent($"Unexpected error: {ex.Message}"),
RequestMessage = request
};
}
}
private class ErrorResponse
{
public string? Message { get; set; }
public string? Error { get; set; }
public Dictionary<string, string[]>? Errors { get; set; }
}
}