/* Kapselt die Kommunikation mit der API für den Catalog-Endpunkt. Bietet Methoden für CRUD-Operationen auf Catalog-Daten */ using System.Net; using System.Net.Http.Json; using System.Text.Json; using DbFirst.BlazorWasm.Models; namespace DbFirst.BlazorWasm.Services; public class CatalogApiClient { private readonly HttpClient _httpClient; private const string Endpoint = "api/catalogs"; public CatalogApiClient(HttpClient httpClient) { _httpClient = httpClient; } public async Task> GetAllAsync() { var result = await _httpClient.GetFromJsonAsync>(Endpoint); return result ?? new List(); } public async Task GetByIdAsync(int id) { return await _httpClient.GetFromJsonAsync($"{Endpoint}/{id}"); } public async Task> CreateAsync(CatalogWriteDto dto) { var response = await _httpClient.PostAsJsonAsync(Endpoint, dto); if (response.IsSuccessStatusCode) { var payload = await response.Content.ReadFromJsonAsync(); return ApiResult.Ok(payload); } var error = await ReadErrorAsync(response); return ApiResult.Fail(error); } public async Task> UpdateAsync(int id, CatalogWriteDto dto) { var response = await _httpClient.PutAsJsonAsync($"{Endpoint}/{id}", dto); if (response.IsSuccessStatusCode) { return ApiResult.Ok(true); } var error = await ReadErrorAsync(response); return ApiResult.Fail(error); } public async Task> DeleteAsync(int id) { var response = await _httpClient.DeleteAsync($"{Endpoint}/{id}"); if (response.IsSuccessStatusCode) { return ApiResult.Ok(true); } var error = await ReadErrorAsync(response); return ApiResult.Fail(error); } private static async Task ReadErrorAsync(HttpResponseMessage response) { // Liest und analysiert Fehlerdetails aus der API-Antwort. // Gibt eine benutzerfreundliche Fehlermeldung zurück. string? problemTitle = null; string? problemDetail = null; try { var problem = await response.Content.ReadFromJsonAsync(); if (problem != null) { problemTitle = problem.Title; problemDetail = problem.Detail ?? problem.Type; } } catch { // ignore parse errors } var status = response.StatusCode; var reason = response.ReasonPhrase; var body = await response.Content.ReadAsStringAsync(); string detail = problemDetail; if (string.IsNullOrWhiteSpace(detail) && !string.IsNullOrWhiteSpace(body)) { detail = body; } // Friendly overrides if (status == HttpStatusCode.Conflict) { return "Datensatz existiert bereits. Bitte wählen Sie einen anderen Titel."; } if (status == HttpStatusCode.BadRequest && (detail?.Contains("CatTitle cannot be changed", StringComparison.OrdinalIgnoreCase) ?? false)) { return "Titel kann nicht geändert werden."; } return status switch { HttpStatusCode.BadRequest => $"Eingabe ungültig{FormatSuffix(problemTitle, detail, reason)}", HttpStatusCode.NotFound => $"Nicht gefunden{FormatSuffix(problemTitle, detail, reason)}", HttpStatusCode.Conflict => $"Konflikt{FormatSuffix(problemTitle, detail, reason)}", HttpStatusCode.Unauthorized => $"Nicht autorisiert{FormatSuffix(problemTitle, detail, reason)}", HttpStatusCode.Forbidden => $"Nicht erlaubt{FormatSuffix(problemTitle, detail, reason)}", HttpStatusCode.InternalServerError => $"Serverfehler{FormatSuffix(problemTitle, detail, reason)}", _ => $"Fehler {(int)status} {reason ?? string.Empty}{FormatSuffix(problemTitle, detail, reason)}" }; } private static string FormatSuffix(string? title, string? detail, string? reason) { // Formatiert zusätzliche Informationen für Fehlermeldungen. // Kombiniert Titel, Details und Grund in einer lesbaren Form. var parts = new List(); if (!string.IsNullOrWhiteSpace(title)) parts.Add(title); if (!string.IsNullOrWhiteSpace(detail)) parts.Add(detail); if (parts.Count == 0 && !string.IsNullOrWhiteSpace(reason)) parts.Add(reason); if (parts.Count == 0) return string.Empty; return ": " + string.Join(" | ", parts); } } public record ApiResult(bool Success, T? Value, string? Error) { public static ApiResult Ok(T? value) => new(true, value, null); public static ApiResult Fail(string? error) => new(false, default, error); } internal sealed class ProblemDetailsDto { public string? Type { get; set; } public string? Title { get; set; } public string? Detail { get; set; } }