110 lines
4.0 KiB
C#
110 lines
4.0 KiB
C#
using System.Net.Http.Json;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace EnvelopeGenerator.ReceiverUI.Client.Services.Base;
|
|
|
|
/// <summary>
|
|
/// Basisklasse für ALLE API-Services.
|
|
///
|
|
/// WARUM eine Basisklasse?
|
|
/// - Einheitliches Error-Handling: Jeder API-Aufruf wird gleich behandelt
|
|
/// - DRY (Don't Repeat Yourself): Logging, Fehlerbehandlung, Serialisierung nur einmal
|
|
/// - Einfache Erweiterung: Retry-Logik, Token-Refresh etc. nur hier ändern
|
|
/// </summary>
|
|
public abstract class ApiServiceBase
|
|
{
|
|
protected readonly HttpClient Http;
|
|
protected readonly ILogger Logger;
|
|
|
|
protected ApiServiceBase(HttpClient http, ILogger logger)
|
|
{
|
|
Http = http;
|
|
Logger = logger;
|
|
}
|
|
|
|
/// <summary>
|
|
/// GET-Request mit Deserialisierung.
|
|
/// Alle API GET-Aufrufe gehen durch diese Methode.
|
|
/// </summary>
|
|
protected async Task<ApiResponse<T>> GetAsync<T>(string endpoint, CancellationToken ct = default)
|
|
{
|
|
try
|
|
{
|
|
var response = await Http.GetAsync(endpoint, ct);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
var errorBody = await response.Content.ReadAsStringAsync(ct);
|
|
Logger.LogWarning("GET {Endpoint} failed: {Status} - {Body}",
|
|
endpoint, (int)response.StatusCode, errorBody);
|
|
return ApiResponse<T>.Failure((int)response.StatusCode, errorBody);
|
|
}
|
|
|
|
var data = await response.Content.ReadFromJsonAsync<T>(cancellationToken: ct);
|
|
return ApiResponse<T>.Success(data!, (int)response.StatusCode);
|
|
}
|
|
catch (HttpRequestException ex)
|
|
{
|
|
Logger.LogError(ex, "HTTP error calling GET {Endpoint}", endpoint);
|
|
return ApiResponse<T>.Failure(0, "Verbindung zum Server fehlgeschlagen.");
|
|
}
|
|
catch (TaskCanceledException)
|
|
{
|
|
Logger.LogWarning("GET {Endpoint} was cancelled", endpoint);
|
|
return ApiResponse<T>.Failure(0, "Anfrage abgebrochen.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// POST-Request mit Body und Response-Deserialisierung.
|
|
/// </summary>
|
|
protected async Task<ApiResponse<TResponse>> PostAsync<TRequest, TResponse>(
|
|
string endpoint, TRequest body, CancellationToken ct = default)
|
|
{
|
|
try
|
|
{
|
|
var response = await Http.PostAsJsonAsync(endpoint, body, ct);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
var errorBody = await response.Content.ReadAsStringAsync(ct);
|
|
Logger.LogWarning("POST {Endpoint} failed: {Status} - {Body}",
|
|
endpoint, (int)response.StatusCode, errorBody);
|
|
return ApiResponse<TResponse>.Failure((int)response.StatusCode, errorBody);
|
|
}
|
|
|
|
var data = await response.Content.ReadFromJsonAsync<TResponse>(cancellationToken: ct);
|
|
return ApiResponse<TResponse>.Success(data!, (int)response.StatusCode);
|
|
}
|
|
catch (HttpRequestException ex)
|
|
{
|
|
Logger.LogError(ex, "HTTP error calling POST {Endpoint}", endpoint);
|
|
return ApiResponse<TResponse>.Failure(0, "Verbindung zum Server fehlgeschlagen.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// POST-Request ohne Response-Body (z.B. Logout).
|
|
/// </summary>
|
|
protected async Task<ApiResponse> PostAsync<TRequest>(
|
|
string endpoint, TRequest body, CancellationToken ct = default)
|
|
{
|
|
try
|
|
{
|
|
var response = await Http.PostAsJsonAsync(endpoint, body, ct);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
var errorBody = await response.Content.ReadAsStringAsync(ct);
|
|
return ApiResponse.Failure((int)response.StatusCode, errorBody);
|
|
}
|
|
|
|
return ApiResponse.Success((int)response.StatusCode);
|
|
}
|
|
catch (HttpRequestException ex)
|
|
{
|
|
Logger.LogError(ex, "HTTP error calling POST {Endpoint}", endpoint);
|
|
return ApiResponse.Failure(0, "Verbindung zum Server fehlgeschlagen.");
|
|
}
|
|
}
|
|
} |