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);
}
}
}
}