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.
This commit is contained in:
60
DbFirst.BlazorWebApp/Services/ApiClientHelper.cs
Normal file
60
DbFirst.BlazorWebApp/Services/ApiClientHelper.cs
Normal file
@@ -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<string> ReadErrorAsync(HttpResponseMessage response)
|
||||||
|
{
|
||||||
|
string? problemTitle = null;
|
||||||
|
string? problemDetail = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var problem = await response.Content.ReadFromJsonAsync<ProblemDetailsDto>();
|
||||||
|
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<string>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ public class CatalogApiClient
|
|||||||
return ApiResult<CatalogReadDto?>.Ok(payload);
|
return ApiResult<CatalogReadDto?>.Ok(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
var error = await ReadErrorAsync(response);
|
var error = await ApiClientHelper.ReadErrorAsync(response);
|
||||||
return ApiResult<CatalogReadDto?>.Fail(error);
|
return ApiResult<CatalogReadDto?>.Fail(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ public class CatalogApiClient
|
|||||||
return ApiResult<bool>.Ok(true);
|
return ApiResult<bool>.Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var error = await ReadErrorAsync(response);
|
var error = await ApiClientHelper.ReadErrorAsync(response);
|
||||||
return ApiResult<bool>.Fail(error);
|
return ApiResult<bool>.Fail(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,68 +58,9 @@ public class CatalogApiClient
|
|||||||
return ApiResult<bool>.Ok(true);
|
return ApiResult<bool>.Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var error = await ReadErrorAsync(response);
|
var error = await ApiClientHelper.ReadErrorAsync(response);
|
||||||
return ApiResult<bool>.Fail(error);
|
return ApiResult<bool>.Fail(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string> ReadErrorAsync(HttpResponseMessage response)
|
|
||||||
{
|
|
||||||
string? problemTitle = null;
|
|
||||||
string? problemDetail = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var problem = await response.Content.ReadFromJsonAsync<ProblemDetailsDto>();
|
|
||||||
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<string>();
|
|
||||||
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<T>(bool Success, T? Value, string? Error)
|
public record ApiResult<T>(bool Success, T? Value, string? Error)
|
||||||
@@ -128,9 +69,3 @@ public record ApiResult<T>(bool Success, T? Value, string? Error)
|
|||||||
public static ApiResult<T> Fail(string? error) => new(false, default, error);
|
public static ApiResult<T> 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; }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -45,27 +45,10 @@ public class MassDataApiClient
|
|||||||
return ApiResult<MassDataReadDto?>.Ok(payload);
|
return ApiResult<MassDataReadDto?>.Ok(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
var error = await ReadErrorAsync(response);
|
var error = await ApiClientHelper.ReadErrorAsync(response);
|
||||||
return ApiResult<MassDataReadDto?>.Fail(error);
|
return ApiResult<MassDataReadDto?>.Fail(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string> ReadErrorAsync(HttpResponseMessage response)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var problem = await response.Content.ReadFromJsonAsync<ProblemDetailsDto>();
|
|
||||||
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<MassDataReadDto?> GetByCustomerNameAsync(string customerName)
|
public async Task<MassDataReadDto?> GetByCustomerNameAsync(string customerName)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(customerName))
|
if (string.IsNullOrWhiteSpace(customerName))
|
||||||
|
|||||||
Reference in New Issue
Block a user