using System.Text.Json; using System.Text.Json.Serialization; namespace FileShare_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"); } /// /// 将 JSON 反序列化为目标类型,反序列化失败或结果为 null 时抛出 。 /// /// 目标类型。 /// JSON 字符串。 /// 来源标识(body 或 query),用于异常消息。 /// 反序列化后的实例。 /// JSON 无法绑定到目标类型时抛出。 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); } } } }