using System.Text.Json; using System.Text.Json.Serialization; namespace Avalonia_Services.Core { /// /// Binds unified endpoint request models from JSON bodies or query parameters. /// internal static class ServiceRequestBinder { private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web) { NumberHandling = JsonNumberHandling.AllowReadingFromString, }; /// /// Bind a JSON request body. Empty bodies are treated as an empty JSON object. /// public static T BindBody(ServiceEndpointContext context) { var json = string.IsNullOrWhiteSpace(context.Body) ? "{}" : context.Body; return Deserialize(json, "body"); } /// /// Bind route and query parameters to a request DTO. /// public static T BindQuery(ServiceEndpointContext context) { var values = new Dictionary(context.Query, StringComparer.OrdinalIgnoreCase); foreach (var routeValue in context.RouteValues) { values[routeValue.Key] = routeValue.Value; } var json = JsonSerializer.Serialize(values, JsonOptions); return Deserialize(json, "query"); } private static T Deserialize(string json, string source) { try { return JsonSerializer.Deserialize(json, JsonOptions) ?? throw new ArgumentException($"Request {source} cannot be bound to {typeof(T).Name}."); } catch (JsonException ex) { throw new ArgumentException($"Request {source} cannot be bound to {typeof(T).Name}.", ex); } } } }