Refactor HTTP response handling and add JSON support

Enhanced `HandleResponseAsync` to return response body as a string and log successful responses. Introduced `JsonOptions` for consistent JSON serialization/deserialization. Added a generic `Deserialize<T>` method for deserializing JSON responses. Updated method signatures to support nullable reference types.
This commit is contained in:
2026-05-20 11:24:01 +02:00
parent d37eda0d6d
commit 8976620205

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Reflection; using System.Reflection;
using System.Text.Json;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -73,18 +74,22 @@ namespace ReC.Client
} }
/// <summary> /// <summary>
/// Logs the outcome of an HTTP response. Throws a <see cref="ReCApiException"/> when the /// JSON serializer options used when deserializing API responses.
/// response indicates a non-success status code; otherwise (optionally) writes an informational /// </summary>
/// log entry containing the request and response details. public static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
};
/// <summary>
/// Reads the response body and logs the outcome. Throws a <see cref="ReCApiException"/> when
/// the response indicates a non-success status code.
/// </summary> /// </summary>
/// <param name="response">The HTTP response to inspect.</param>
/// <param name="logger">An optional logger used to record the outcome. May be <see langword="null"/>.</param>
/// <param name="logSuccess">When <see langword="false"/>, successful responses are not logged.</param>
/// <param name="cancel">A token to cancel the operation.</param>
#if NETFRAMEWORK #if NETFRAMEWORK
public static async Task HandleResponseAsync(HttpResponseMessage response, ILogger logger = null, bool logSuccess = true, CancellationToken cancel = default) public static async Task<string> HandleResponseAsync(HttpResponseMessage response, ILogger logger = null, bool logSuccess = true, CancellationToken cancel = default)
#else #else
public static async Task HandleResponseAsync(HttpResponseMessage response, ILogger? logger = null, bool logSuccess = true, CancellationToken cancel = default) public static async Task<string?> HandleResponseAsync(HttpResponseMessage response, ILogger? logger = null, bool logSuccess = true, CancellationToken cancel = default)
#endif #endif
{ {
var request = response.RequestMessage; var request = response.RequestMessage;
@@ -92,20 +97,6 @@ namespace ReC.Client
var uri = request?.RequestUri; var uri = request?.RequestUri;
var statusCode = (int)response.StatusCode; var statusCode = (int)response.StatusCode;
if (response.IsSuccessStatusCode)
{
if (logSuccess)
{
logger?.LogInformation(
"ReC API request succeeded. {Method} {Uri} -> {StatusCode} ({ReasonPhrase})",
method,
uri,
statusCode,
response.ReasonPhrase);
}
return;
}
#if NETFRAMEWORK #if NETFRAMEWORK
string body = null; string body = null;
#else #else
@@ -123,15 +114,44 @@ namespace ReC.Client
} }
catch catch
{ {
// Swallow body read failures; status info is still propagated.
} }
} }
if (response.IsSuccessStatusCode)
{
if (logSuccess)
{
logger?.LogInformation(
"ReC API request succeeded. {Method} {Uri} -> {StatusCode} ({ReasonPhrase})",
method,
uri,
statusCode,
response.ReasonPhrase);
}
return body;
}
var message = $"ReC API request failed with status {statusCode} ({response.ReasonPhrase}). " var message = $"ReC API request failed with status {statusCode} ({response.ReasonPhrase}). "
+ $"{method} {uri}" + $"{method} {uri}"
+ (string.IsNullOrWhiteSpace(body) ? string.Empty : $": {body}"); + (string.IsNullOrWhiteSpace(body) ? string.Empty : $": {body}");
throw new ReCApiException(message, response.StatusCode, response.ReasonPhrase, body, method, uri); throw new ReCApiException(message, response.StatusCode, response.ReasonPhrase, body, method, uri);
} }
/// <summary>
/// Deserializes a JSON body string into <typeparamref name="T"/>.
/// </summary>
#if NETFRAMEWORK
public static T Deserialize<T>(string body)
#else
public static T? Deserialize<T>(string? body)
#endif
{
if (string.IsNullOrWhiteSpace(body))
return default;
return JsonSerializer.Deserialize<T>(body, JsonOptions);
}
} }
} }