using Avalonia_Services.Core; using Microsoft.AspNetCore.Authorization; using AspNetCoreFilterContext = Microsoft.AspNetCore.Http.EndpointFilterInvocationContext; using AspNetCoreFilterDelegate = Microsoft.AspNetCore.Http.EndpointFilterDelegate; // 解决与 ASP.NET Core 同名类型的冲突 using UnifiedFilter = Avalonia_Services.Core.IEndpointFilter; namespace Avalonia_API.Extensions { /// /// 将 Avalonia-Services 的统一端点映射到 ASP.NET Core Minimal API。 /// 支持鉴权、过滤器、中间件的完整 ASP.NET Core 管道。 /// public static class UnifiedEndpointExtensions { /// /// 将 ServiceEndpointCollection 中的所有端点注册到 ASP.NET Core 路由。 /// public static IEndpointRouteBuilder MapUnifiedEndpoints( this IEndpointRouteBuilder routeBuilder, ServiceEndpointCollection endpoints, IServiceProvider serviceProvider) { var apiGroup = routeBuilder.MapGroup("/"); foreach (var endpoint in endpoints.ForHost(EndpointHostTarget.Api)) { var routeHandlerBuilder = MapEndpoint(apiGroup, endpoint, serviceProvider); // 全局过滤器 → ASP.NET Core Endpoint Filters foreach (var globalFilter in endpoints.GlobalFilters) { routeHandlerBuilder.AddEndpointFilter( async (context, next) => await ConvertFilterAsync(globalFilter, context, next)); } // 端点专属过滤器 foreach (var filter in endpoint.Filters) { routeHandlerBuilder.AddEndpointFilter( async (context, next) => await ConvertFilterAsync(filter, context, next)); } // 鉴权(使用 ASP.NET Core 原生鉴权机制) if (endpoint.RequireAuthorization) { if (endpoint.Roles.Count > 0) { routeHandlerBuilder.RequireAuthorization(new AuthorizeAttribute { Roles = string.Join(',', endpoint.Roles), }); } else if (!string.IsNullOrEmpty(endpoint.Policy)) { routeHandlerBuilder.RequireAuthorization(endpoint.Policy); } else { routeHandlerBuilder.RequireAuthorization(); } } if (!string.IsNullOrEmpty(endpoint.Name)) { routeHandlerBuilder.WithName(endpoint.Name); } if (!string.IsNullOrEmpty(endpoint.OpenApiTag)) { routeHandlerBuilder.WithTags(endpoint.OpenApiTag); } if (!string.IsNullOrEmpty(endpoint.OpenApiDescription)) { routeHandlerBuilder.WithDescription(endpoint.OpenApiDescription); } if (!string.IsNullOrWhiteSpace(endpoint.OpenApiSummary)) { routeHandlerBuilder.WithSummary(endpoint.OpenApiSummary); } if (endpoint.OpenApiRequestType is not null) { routeHandlerBuilder.Accepts(endpoint.OpenApiRequestType, "application/json"); } if (endpoint.OpenApiResponseType is not null) { routeHandlerBuilder.Produces(200, endpoint.OpenApiResponseType, "application/json"); } } return routeBuilder; } /// /// 根据端点的 HTTP 方法(GET/POST/PUT/DELETE)将其映射到 ASP.NET Core 路由。 /// /// 路由组。 /// 统一端点定义。 /// 服务提供程序。 /// 路由处理器构建器,用于叠加过滤器等配置。 private static RouteHandlerBuilder MapEndpoint( IEndpointRouteBuilder group, ServiceEndpoint endpoint, IServiceProvider serviceProvider) { var handler = CreateAspNetCoreHandler(endpoint.Handler, serviceProvider); return endpoint.HttpMethod.ToUpperInvariant() switch { "GET" => group.MapGet(endpoint.Pattern, handler), "POST" => group.MapPost(endpoint.Pattern, handler), "PUT" => group.MapPut(endpoint.Pattern, handler), "DELETE" => group.MapDelete(endpoint.Pattern, handler), _ => group.MapGet(endpoint.Pattern, handler), }; } /// /// 创建适配 ASP.NET Core 的委托处理器,将统一处理器包装为 ASP.NET Core 可识别的委托。 /// /// 统一端点处理器。 /// 服务提供程序。 /// ASP.NET Core 兼容的委托。 private static Delegate CreateAspNetCoreHandler( Func> unifiedHandler, IServiceProvider serviceProvider) { return async (HttpContext httpContext) => { var ctx = httpContext.Items["UnifiedContext"] as ServiceEndpointContext ?? await BuildContextFromHttpContext(httpContext); ctx.Items["ServiceProvider"] = serviceProvider; ctx.Items["User"] = httpContext.User; httpContext.Items["UnifiedContext"] = ctx; var result = await unifiedHandler(ctx); // 同步响应状态 httpContext.Response.StatusCode = ctx.StatusCode; foreach (var kvp in ctx.ResponseHeaders) { httpContext.Response.Headers[kvp.Key] = kvp.Value; } return result is not null ? Results.Json(result) : Results.Ok(); }; } /// /// 从 ASP.NET Core 的 HttpContext 构建统一的 ServiceEndpointContext, /// 提取路径、方法、请求头、查询参数和请求体。 /// /// ASP.NET Core 的 HttpContext。 /// 构建好的统一端点上下文。 private static async Task BuildContextFromHttpContext(HttpContext httpContext) { var ctx = new ServiceEndpointContext { Path = httpContext.Request.Path.Value ?? "/", Method = httpContext.Request.Method, StatusCode = 200, }; foreach (var header in httpContext.Request.Headers) { ctx.Headers[header.Key] = header.Value.ToString(); } foreach (var query in httpContext.Request.Query) { ctx.Query[query.Key] = query.Value.ToString(); } if (httpContext.Request.ContentLength > 0) { using var reader = new StreamReader(httpContext.Request.Body); ctx.Body = await reader.ReadToEndAsync(); } ctx.Items["HttpContext"] = httpContext; ctx.Items["User"] = httpContext.User; return ctx; } /// /// 将统一过滤器转换为 ASP.NET Core 端点过滤器, /// 在调用统一过滤器前后桥接上下文和状态。 /// /// 统一过滤器。 /// ASP.NET Core 过滤器调用上下文。 /// ASP.NET Core 过滤器管道中的下一个委托。 /// 过滤器执行结果,可能包含短路响应体。 private static async ValueTask ConvertFilterAsync( UnifiedFilter unifiedFilter, AspNetCoreFilterContext aspContext, AspNetCoreFilterDelegate aspNext) { var httpContext = aspContext.HttpContext; var ctx = httpContext.Items["UnifiedContext"] as ServiceEndpointContext ?? await BuildContextFromHttpContext(httpContext); httpContext.Items["UnifiedContext"] = ctx; object? nextResult = null; await unifiedFilter.InvokeAsync(ctx, async (c) => { httpContext.Response.StatusCode = c.StatusCode; foreach (var kvp in c.ResponseHeaders) { httpContext.Response.Headers[kvp.Key] = kvp.Value; } nextResult = await aspNext(aspContext); }); if (ctx.ResponseBody is not null) { return Results.Json(ctx.ResponseBody, statusCode: ctx.StatusCode); } return nextResult; } } }