slogan3

Ontwerp een C# API-client voor RESTful services met foutafhandeling

description

Met deze prompt kan de gebruiker een betrouwbare en efficiënte C# API-client ontwikkelen die eenvoudig communiceert met RESTful services. Het lost problemen op zoals het correct verwerken van JSON-data en het afhandelen van netwerkfouten, wat de kwaliteit en stabiliteit van de applicatie verbetert. Het is beter dan alternatieven doordat het praktische voorbeelden en best practices biedt, waardoor ontwikkeltijd wordt verkort en fouten worden voorkomen.

prompt

try_prompt

Help me een C# API-client te ontwerpen die communiceert met de volgende RESTful service: <voer de API-basis-URL in>. Beschrijf hoe ik HTTP-verzoeken (GET, POST, PUT, DELETE) kan implementeren, inclusie ... more
generate_helper
disclaimerOnPageApi image_legal_disclaimer...

ai_answers

provider: openai
model: gpt-4.1-nano
Natuurlijk! Hier is een uitgebreide gids voor het ontwerpen van een C# API-client die communiceert met een RESTful service, inclusief voorbeelden voor HTTP-verzoeken, JSON-serialisatie/deserialisatie, foutafhandeling en herhaalmechanismen. --- ### 1. **HttpClient opzetten** Gebruik `HttpClient` voor het versturen van HTTP-verzoeken. Het is aan te raden om `HttpClient` één keer te initialiseren en te hergebruiken (bijvoorbeeld via dependency injection of een singleton). ```csharp public class ApiClient { private readonly HttpClient _httpClient; public ApiClient(HttpClient httpClient) { _httpClient = httpClient; _httpClient.BaseAddress = new Uri("https://api.example.com/v1/"); } } ``` --- ### 2. **JSON-serialisatie en -deserialisatie** Gebruik `System.Text.Json` voor JSON-conversie (beschikbaar vanaf .NET Core 3.0 en later). Instellen kan via `JsonSerializerOptions` bijvoorbeeld voor camelCase. ```csharp private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNameCaseInsensitive = true }; ``` **Serialiseren:** ```csharp string jsonString = JsonSerializer.Serialize(object, _jsonOptions); ``` **Deserialiseren:** ```csharp var myObject = JsonSerializer.Deserialize<MyType>(jsonString, _jsonOptions); ``` --- ### 3. **HTTP-verzoeken implementeren** Hier zijn voorbeelden voor GET, POST, PUT en DELETE. ```csharp // GET public async Task<T> GetAsync<T>(string endpoint) { var response = await _httpClient.GetAsync(endpoint); response.EnsureSuccessStatusCode(); // Gooit uitzondering bij foutstatus var jsonString = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<T>(jsonString, _jsonOptions); } // POST public async Task<TResponse> PostAsync<TRequest, TResponse>(string endpoint, TRequest data) { var jsonContent = new StringContent( JsonSerializer.Serialize(data, _jsonOptions), Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync(endpoint, jsonContent); response.EnsureSuccessStatusCode(); var jsonString = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<TResponse>(jsonString, _jsonOptions); } // PUT public async Task<TResponse> PutAsync<TRequest, TResponse>(string endpoint, TRequest data) { var jsonContent = new StringContent( JsonSerializer.Serialize(data, _jsonOptions), Encoding.UTF8, "application/json"); var response = await _httpClient.PutAsync(endpoint, jsonContent); response.EnsureSuccessStatusCode(); var jsonString = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<TResponse>(jsonString, _jsonOptions); } // DELETE public async Task DeleteAsync(string endpoint) { var response = await _httpClient.DeleteAsync(endpoint); response.EnsureSuccessStatusCode(); } ``` --- ### 4. **Robuuste foutafhandeling** Gebruik try-catch blokken en controleer op HTTP-fouten. ```csharp public async Task<T> GetAsync<T>(string endpoint) { try { var response = await _httpClient.GetAsync(endpoint); if (!response.IsSuccessStatusCode) { // Log of behandel fout throw new HttpRequestException($"Fout bij GET {endpoint}: {response.StatusCode}"); } var jsonString = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<T>(jsonString, _jsonOptions); } catch (HttpRequestException ex) { // Fout afhandelen of herhaalmechanisme throw new Exception($"HTTP-fout: {ex.Message}"); } } ``` --- ### 5. **Herhaalmechanismen (Retry policies)** Gebruik bijvoorbeeld `Polly`, een populaire bibliotheek voor herhaalmechanismen. ```csharp using Polly; using Polly.Retry; public class ApiClient { private readonly AsyncRetryPolicy _retryPolicy; public ApiClient(HttpClient httpClient) { _httpClient = httpClient; _httpClient.BaseAddress = new Uri("https://api.example.com/v1/"); _retryPolicy = Policy .Handle<HttpRequestException>() .OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode) .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), onRetry: (outcome, timespan, retryCount, context) => { // Log of actie bij retry Console.WriteLine($"Retry {retryCount} na {timespan.Seconds} seconden vanwege: {outcome.Exception?.Message ?? outcome.Result.StatusCode.ToString()}"); }); } public async Task<T> GetAsync<T>(string endpoint) { return await _retryPolicy.ExecuteAsync(async () => { var response = await _httpClient.GetAsync(endpoint); if (!response.IsSuccessStatusCode) throw new HttpRequestException($"Statuscode {response.StatusCode}"); var jsonString = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<T>(jsonString, _jsonOptions); }); } } ``` --- ### 6. **Gebruik voorbeeld** ```csharp var httpClient = new HttpClient(); var apiClient = new ApiClient(httpClient); // Voorbeeld GET var result = await apiClient.GetAsync<YourType>("resource/123"); // Voorbeeld POST var newData = new YourType { /* properties */ }; var response = await apiClient.PostAsync<YourType, ResponseType>("resource", newData); ``` --- ### Samenvatting - Gebruik `HttpClient` voor HTTP-verzoeken. - Serialiseer en deserialiseer met `System.Text.Json`. - Implementeer try-catch voor foutafhandeling. - Gebruik Polly voor automatische retries bij falen. - Maak gebruik van asynchrone methoden (`async/await`) voor efficiënte I/O. Wil je nog een voorbeeld van volledige code of hulp bij een specifiek onderdeel?
provider: deepseek
model: deepseek-chat
Zal ik je helpen een robuuste C# API-client te ontwerpen. Hier is een complete implementatie: ## 1. Basisklassen en Models ```csharp using System; using System.Net; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; // Data model voor voorbeeld public class User { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } } // Response wrapper voor consistente error handling public class ApiResponse<T> { public bool IsSuccess { get; set; } public T Data { get; set; } public string ErrorMessage { get; set; } public HttpStatusCode StatusCode { get; set; } } // Custom exceptions public class ApiException : Exception { public HttpStatusCode StatusCode { get; } public ApiException(string message, HttpStatusCode statusCode) : base(message) { StatusCode = statusCode; } } ``` ## 2. Hoofd API Client Implementatie ```csharp public class ExampleApiClient : IDisposable { private readonly HttpClient _httpClient; private readonly JsonSerializerOptions _jsonOptions; private const int MaxRetries = 3; private const int RetryDelayMs = 1000; public ExampleApiClient(string baseUrl) { _httpClient = new HttpClient { BaseAddress = new Uri(baseUrl), Timeout = TimeSpan.FromSeconds(30) }; // Stel default headers in _httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); _httpClient.DefaultRequestHeaders.Add("User-Agent", "ExampleApiClient/1.0"); _jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = false }; } // Authenticatie toevoegen (optioneel) public void SetAuthentication(string token) { _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); } } ``` ## 3. HTTP Method Implementaties ```csharp public class ExampleApiClient : IDisposable { // GET request public async Task<ApiResponse<T>> GetAsync<T>(string endpoint, CancellationToken cancellationToken = default) { return await ExecuteWithRetryAsync<T>(async () => { using var response = await _httpClient.GetAsync(endpoint, cancellationToken); return await ProcessResponse<T>(response); }); } // POST request public async Task<ApiResponse<T>> PostAsync<T>(string endpoint, object data, CancellationToken cancellationToken = default) { return await ExecuteWithRetryAsync<T>(async () => { var json = JsonSerializer.Serialize(data, _jsonOptions); var content = new StringContent(json, Encoding.UTF8, "application/json"); using var response = await _httpClient.PostAsync(endpoint, content, cancellationToken); return await ProcessResponse<T>(response); }); } // PUT request public async Task<ApiResponse<T>> PutAsync<T>(string endpoint, object data, CancellationToken cancellationToken = default) { return await ExecuteWithRetryAsync<T>(async () => { var json = JsonSerializer.Serialize(data, _jsonOptions); var content = new StringContent(json, Encoding.UTF8, "application/json"); using var response = await _httpClient.PutAsync(endpoint, content, cancellationToken); return await ProcessResponse<T>(response); }); } // DELETE request public async Task<ApiResponse<bool>> DeleteAsync(string endpoint, CancellationToken cancellationToken = default) { return await ExecuteWithRetryAsync<bool>(async () => { using var response = await _httpClient.DeleteAsync(endpoint, cancellationToken); return await ProcessResponse<bool>(response); }); } } ``` ## 4. Response Verwerking en Error Handling ```csharp public class ExampleApiClient : IDisposable { private async Task<ApiResponse<T>> ProcessResponse<T>(HttpResponseMessage response) { var apiResponse = new ApiResponse<T> { StatusCode = response.StatusCode, IsSuccess = response.IsSuccessStatusCode }; try { var content = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { if (!string.IsNullOrEmpty(content)) { apiResponse.Data = JsonSerializer.Deserialize<T>(content, _jsonOptions); } } else { apiResponse.ErrorMessage = $"HTTP {response.StatusCode}: {content}"; // Gooi exception voor kritieke errors if ((int)response.StatusCode >= 400) { throw new ApiException(apiResponse.ErrorMessage, response.StatusCode); } } } catch (JsonException jsonEx) { apiResponse.IsSuccess = false; apiResponse.ErrorMessage = $"JSON deserialisatie fout: {jsonEx.Message}"; throw new ApiException(apiResponse.ErrorMessage, HttpStatusCode.InternalServerError); } return apiResponse; } } ``` ## 5. Retry Mechanism ```csharp public class ExampleApiClient : IDisposable { private async Task<ApiResponse<T>> ExecuteWithRetryAsync<T>(Func<Task<ApiResponse<T>>> operation) { Exception lastException = null; for (int attempt = 1; attempt <= MaxRetries; attempt++) { try { return await operation(); } catch (HttpRequestException ex) when (IsTransientError(ex)) { lastException = ex; if (attempt < MaxRetries) { await Task.Delay(RetryDelayMs * attempt); continue; } } catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException) { lastException = ex; if (attempt < MaxRetries) { await Task.Delay(RetryDelayMs * attempt); continue; } } } throw new ApiException($"Request mislukt na {MaxRetries} pogingen", HttpStatusCode.RequestTimeout); } private bool IsTransientError(HttpRequestException ex) { // Retry bij network errors, timeouts, etc. return ex.Message.Contains("timed out") || ex.Message.Contains("network") || ex.InnerException is TimeoutException; } } ``` ## 6. Gebruiksvoorbeeld ```csharp public class Program { public static async Task Main() { // Client initialiseren using var apiClient = new ExampleApiClient("https://api.example.com/v1/"); try { // GET voorbeeld - alle users ophalen var usersResponse = await apiClient.GetAsync<List<User>>("users"); if (usersResponse.IsSuccess) { foreach (var user in usersResponse.Data) { Console.WriteLine($"User: {user.Name}, Email: {user.Email}"); } } // POST voorbeeld - nieuwe user aanmaken var newUser = new User { Name = "Jan Jansen", Email = "jan@example.com" }; var createResponse = await apiClient.PostAsync<User>("users", newUser); if (createResponse.IsSuccess) { Console.WriteLine($"User aangemaakt met ID: {createResponse.Data.Id}"); } // PUT voorbeeld - user updaten var updatedUser = new User { Name = "Jan Janssen", Email = "jan.janssen@example.com" }; var updateResponse = await apiClient.PutAsync<User>($"users/{createResponse.Data.Id}", updatedUser); // DELETE voorbeeld - user verwijderen var deleteResponse = await apiClient.DeleteAsync($"users/{createResponse.Data.Id}"); if (deleteResponse.IsSuccess) { Console.WriteLine("User succesvol verwijderd"); } } catch (ApiException ex) { Console.WriteLine($"API Fout ({ex.StatusCode}): {ex.Message}"); } catch (Exception ex) { Console.WriteLine($"Onverwachte fout: {ex.Message}"); } } } ``` ## 7. Dispose Pattern ```csharp public class ExampleApiClient : IDisposable { private bool _disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed && disposing) { _httpClient?.Dispose(); _disposed = true; } } } ``` ## Belangrijke Kenmerken: 1. **Async/Await**: Alle methoden zijn asynchroon voor betere performance 2. **Error Handling**: Uitgebreide foutafhandeling met custom exceptions 3. **Retry Logic**: Automatische herpogingen bij transient errors 4. **JSON Serialisatie**: Gebruik van System.Text.Json voor hoge performance 5. **Cancellation Support**: Ondersteuning voor cancellation tokens 6. **Disposable**: Juiste resource management met Dispose pattern 7. **Type Safety**: Generieke methoden voor type-veilige responses Deze client is productie-ready en bevat alle best practices voor een robuuste API-integratie!