From a0e0d7ed0351e008f7c8d4c2c640d54cdda4d4ba Mon Sep 17 00:00:00 2001 From: OlgunR Date: Wed, 15 Apr 2026 13:55:57 +0200 Subject: [PATCH] Centralize API error handling in ApiClientHelper Refactored error handling logic for API responses into a new static ApiClientHelper class, consolidating the ReadErrorAsync method and ProblemDetailsDto. Updated CatalogApiClient and MassDataApiClient to use the shared helper, removing redundant local methods and classes. This change improves consistency, reduces duplication, and streamlines error message formatting across the application. --- .../Services/ApiClientHelper.cs | 60 ++++++++++++++++ .../Services/CatalogApiClient.cs | 71 +------------------ .../Services/MassDataApiClient.cs | 19 +---- 3 files changed, 64 insertions(+), 86 deletions(-) create mode 100644 DbFirst.BlazorWebApp/Services/ApiClientHelper.cs diff --git a/DbFirst.BlazorWebApp/Services/ApiClientHelper.cs b/DbFirst.BlazorWebApp/Services/ApiClientHelper.cs new file mode 100644 index 0000000..8c803d4 --- /dev/null +++ b/DbFirst.BlazorWebApp/Services/ApiClientHelper.cs @@ -0,0 +1,60 @@ +using System.Net; +using System.Net.Http.Json; + +namespace DbFirst.BlazorWebApp.Services; + +internal sealed class ProblemDetailsDto +{ + public string? Type { get; set; } + public string? Title { get; set; } + public string? Detail { get; set; } +} + +internal static class ApiClientHelper +{ + public static async Task ReadErrorAsync(HttpResponseMessage response) + { + 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 { } + + 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; + + 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) + { + 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); + } +} \ No newline at end of file diff --git a/DbFirst.BlazorWebApp/Services/CatalogApiClient.cs b/DbFirst.BlazorWebApp/Services/CatalogApiClient.cs index 1c4590a..b928508 100644 --- a/DbFirst.BlazorWebApp/Services/CatalogApiClient.cs +++ b/DbFirst.BlazorWebApp/Services/CatalogApiClient.cs @@ -34,7 +34,7 @@ public class CatalogApiClient return ApiResult.Ok(payload); } - var error = await ReadErrorAsync(response); + var error = await ApiClientHelper.ReadErrorAsync(response); return ApiResult.Fail(error); } @@ -46,7 +46,7 @@ public class CatalogApiClient return ApiResult.Ok(true); } - var error = await ReadErrorAsync(response); + var error = await ApiClientHelper.ReadErrorAsync(response); return ApiResult.Fail(error); } @@ -58,68 +58,9 @@ public class CatalogApiClient return ApiResult.Ok(true); } - var error = await ReadErrorAsync(response); + var error = await ApiClientHelper.ReadErrorAsync(response); return ApiResult.Fail(error); } - - private static async Task ReadErrorAsync(HttpResponseMessage response) - { - 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 - { - } - - 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; - } - - 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) - { - 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) @@ -128,9 +69,3 @@ public record ApiResult(bool Success, T? Value, string? Error) 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; } -} diff --git a/DbFirst.BlazorWebApp/Services/MassDataApiClient.cs b/DbFirst.BlazorWebApp/Services/MassDataApiClient.cs index 6854dce..4735881 100644 --- a/DbFirst.BlazorWebApp/Services/MassDataApiClient.cs +++ b/DbFirst.BlazorWebApp/Services/MassDataApiClient.cs @@ -45,27 +45,10 @@ public class MassDataApiClient return ApiResult.Ok(payload); } - var error = await ReadErrorAsync(response); + var error = await ApiClientHelper.ReadErrorAsync(response); return ApiResult.Fail(error); } - private static async Task ReadErrorAsync(HttpResponseMessage response) - { - try - { - var problem = await response.Content.ReadFromJsonAsync(); - if (problem != null && !string.IsNullOrWhiteSpace(problem.Title)) - return problem.Detail ?? problem.Title ?? response.ReasonPhrase ?? "Unbekannter Fehler"; - } - catch { } - - var body = await response.Content.ReadAsStringAsync(); - if (!string.IsNullOrWhiteSpace(body)) - return body; - - return $"Fehler {(int)response.StatusCode} {response.ReasonPhrase}".Trim(); - } - public async Task GetByCustomerNameAsync(string customerName) { if (string.IsNullOrWhiteSpace(customerName))