- 新增 IApiResponse 统一响应契约,覆盖普通响应和分页响应
- 扩展端点映射,支持 IApiResponse 和带请求 DTO 的 MapXxx 重载
- 增加 Body、Query、Route Values 到请求 DTO 的自动绑定
- 增加 PC 端路由模式匹配,支持 {id} 和 {id:int}
- API 与 PC 端点上下文补充路由参数传递
- 调整 OpenAPI 请求类型处理,避免 GET/DELETE 被标记为 JSON Body
- 鉴权端点迁移到强类型请求绑定和 IApiResponse 返回
- 增加统一端点文件流响应支持
54 lines
1.8 KiB
C#
54 lines
1.8 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace Avalonia_Services.Core
|
|
{
|
|
/// <summary>
|
|
/// Binds unified endpoint request models from JSON bodies or query parameters.
|
|
/// </summary>
|
|
internal static class ServiceRequestBinder
|
|
{
|
|
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
|
|
{
|
|
NumberHandling = JsonNumberHandling.AllowReadingFromString,
|
|
};
|
|
|
|
/// <summary>
|
|
/// Bind a JSON request body. Empty bodies are treated as an empty JSON object.
|
|
/// </summary>
|
|
public static T BindBody<T>(ServiceEndpointContext context)
|
|
{
|
|
var json = string.IsNullOrWhiteSpace(context.Body) ? "{}" : context.Body;
|
|
return Deserialize<T>(json, "body");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind route and query parameters to a request DTO.
|
|
/// </summary>
|
|
public static T BindQuery<T>(ServiceEndpointContext context)
|
|
{
|
|
var values = new Dictionary<string, string>(context.Query, StringComparer.OrdinalIgnoreCase);
|
|
foreach (var routeValue in context.RouteValues)
|
|
{
|
|
values[routeValue.Key] = routeValue.Value;
|
|
}
|
|
|
|
var json = JsonSerializer.Serialize(values, JsonOptions);
|
|
return Deserialize<T>(json, "query");
|
|
}
|
|
|
|
private static T Deserialize<T>(string json, string source)
|
|
{
|
|
try
|
|
{
|
|
return JsonSerializer.Deserialize<T>(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);
|
|
}
|
|
}
|
|
}
|
|
}
|