From 8b0a8f783c29ba7d0aca627a3f82898aed41a6ce Mon Sep 17 00:00:00 2001 From: xRain Date: Mon, 17 Aug 2026 09:16:42 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20SimApiExtensions=20=E7=A7=BB?= =?UTF-8?q?=E5=85=A5=E6=A0=B9=E5=8C=85=20simapi=20=E5=B9=B6=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E9=9D=99=E6=80=81=E7=B1=BB=EF=BC=8C=E5=88=A0=E9=99=A4?= =?UTF-8?q?=20extensions=20=E5=AD=90=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src/extensions/SimApiExtensions.cj 删除,内容合并进 src/SimApiExtensions.cj(package simapi) - 接口+extend 改为静态类 SimApiExtensions: SimApiExtensions.addSimApi(builder, configure) / useSimApi(host)(对齐 .NET 根命名空间静态类) - 私有辅助 addSimApiCore / addControllers 收敛为私有静态方法 - 用法从 builder.addSimApi{} / host.useSimApi() 改为 SimApiExtensions.addSimApi(builder){} / useSimApi(host) --- README.md | 20 +- src/SimApiExtensions.cj | 353 +++++++++++++++++++++++++- src/extensions/SimApiExtensions.cj | 392 ----------------------------- 3 files changed, 359 insertions(+), 406 deletions(-) delete mode 100644 src/extensions/SimApiExtensions.cj diff --git a/README.md b/README.md index 0265081..407575b 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ import soulsoft_web_routing.* import soulsoft_web_hosting.* import soulsoft_extensions_logging.* import soulsoft_extensions_injection.* -import simapi.extensions.* +import simapi.* import simapi.communications.* main(args: Array) { @@ -25,14 +25,14 @@ main(args: Array) { builder.services.addLogging() // 注册 SimApi 服务(与 addLogging 同样式) - builder.addSimApi { options => + SimApiExtensions.addSimApi(builder) { options => options.enableSimApiAuth = true // Token 认证(未配 Redis 自动用 InMemory) options.enableSimApiCache = true // 缓存 options.enableSimApiException = true // 全局异常拦截 } let host = builder.build() - host.useSimApi() + SimApiExtensions.useSimApi(host) // 业务接口:返回统一响应格式 host.mapGet("hello") { @@ -74,17 +74,17 @@ main(args: Array) { simapi-cj/ ├── cjpm.toml # 包配置 ├── src/ +│ ├── SimApiExtensions.cj # 根包入口:SimApiExtensions 静态类(addSimApi / useSimApi + 内置路由 + 响应封装) │ ├── attributes/ # 声明式注解:@SimApiAuth(鉴权)、@OriginResponse(原样响应) │ ├── authsdk/ # 认证中心 SDK:SimApiAuthClient/Center/Iam + 网关中间件 + DTO │ ├── communications/ # SimApiBaseResponse, PageResponse, SimApiLoginItem, 请求 DTO │ ├── configurations/ # SimApiOptions + 各模块 Option(含 ConfigureSimApiXxx 回调) │ ├── controllers/ # SimApiBaseController, SimApiCommonController, SimApiAuthController(MVC 写法) │ ├── exceptions/ # SimApiException -│ ├── extensions/ # SimApiExtensions(addSimApi / useSimApi + 内置路由 + 响应封装) │ ├── helpers/ # SimApiError, SimApiUtil, SimApiAuth, SimApiCache, SimApiHttpClient, │ │ # SimApiAesUtil(AES-256), SimApiSignChecker(验签), SimApiAesBodyChecker(AES body), -│ │ # SimApiStorage(S3/MinIO, 自实现 SigV4) -│ ├── interfaces/ # ISimApiAuthChecker +│ │ # SimApiStorage(S3/MinIO, 自实现 SigV4), SimApiRequestDelegateFactory, SimApiResultWriter +│ ├── interfaces/ # ISimApiAuthChecker, IBindRequestContext │ ├── logger/ # SimApiLogger, SimApiLoggerProvider(彩色日志) │ ├── macros/ # ReadTomlVersion(编译期读版本号) │ ├── middlewares/ # SimApiExceptionMiddleware, SimApiAuthMiddleware, SimApiRequestLogMiddleware @@ -283,7 +283,7 @@ let resp3 = client.aesSignQuery("/api/data", body: "{\"a\":1}") - 响应体因 soulsoft `HttpResponse.body` 只读不可替换,记录 `Content-Length` 作为替代(C# 用 MemoryStream 捕获) ```cangjie -builder.addSimApi { options => +SimApiExtensions.addSimApi(builder) { options => options.enableRequestLog = true options.simApiRequestLogOptions.showFullHeader = true // 打印完整 Header(默认只打 Token/Query-Id) options.simApiRequestLogOptions.requestStringLogLength = 200 // 请求体字段截断长度(0 不截断) @@ -326,7 +326,7 @@ builder.addSimApi { options => `enableSimApiStorage = true` 时注册 `SimApiStorage`(Scoped,内部自实现 AWS Signature V4,无需 Minio SDK): ```cangjie -builder.addSimApi { options => +SimApiExtensions.addSimApi(builder) { options => options.enableSimApiStorage = true options.configureSimApiStorage { storage => storage.endpoint = "http://192.168.0.2:9000" // 必须 http:// 或 https:// 开头 @@ -388,7 +388,7 @@ class MyAuthChecker <: ISimApiAuthChecker { `enableSimApiAuthGate = true` 时注册 `SimApiAuthClient` / `SimApiAuthCenter` / `SimApiAuthIam` 单例并挂载网关透传中间件: ```cangjie -builder.addSimApi { options => +SimApiExtensions.addSimApi(builder) { options => options.enableSimApiAuthGate = true options.configureSimApiAuthCenter { auth => auth.server = "https://auth.example.com" @@ -420,7 +420,7 @@ iam.checkPermission(profileId, "app:create") // 无权限抛 403 ## SimApiOptions 完整配置 ```cangjie -builder.addSimApi { options => +SimApiExtensions.addSimApi(builder) { options => options.redisConfiguration = "localhost:6379" // Redis(可选,支持 ,password=xxx,db=2) // 功能开关 diff --git a/src/SimApiExtensions.cj b/src/SimApiExtensions.cj index f85c83b..fb68638 100644 --- a/src/SimApiExtensions.cj +++ b/src/SimApiExtensions.cj @@ -2,10 +2,355 @@ * Copyright (c) 2025 SimcuTeam. All rights reserved. * 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。 * - * 文件名对齐 .NET:根目录 SimApiExtensions.cs(namespace SimApi)。 - * 本文件是 simapi 根包的入口锚点:cjpm 要求 src 根目录至少有一个 .cj 文件, - * 否则不会扫描 src 子目录(helpers/extensions/...),整个包将编译为空。 - * 实际扩展入口见 src/extensions/SimApiExtensions.cj(addSimApi / useSimApi)。 + * 对齐 .NET:根命名空间 SimApi 下的静态类 SimApiExtensions(AddSimApi + UseSimApi)。 + * 本文件同时是 simapi 根包的入口锚点:cjpm 要求 src 根目录至少有一个 .cj 文件, + * 否则不会扫描 src 子目录(helpers/controllers/...),整个包将编译为空。 + * + * 使用方式: + * ``` + * let builder = WebHost.createBuilder(args) + * builder.services.addLogging() + * SimApiExtensions.addSimApi(builder) { options => + * options.enableSimApiAuth = true + * } + * let host = builder.build() + * SimApiExtensions.useSimApi(host) + * host.run() + * ``` */ package simapi + +import std.collection.* +import std.convert.* +import std.reflect.* +import std.time.* +import soulsoft_serialization.* +import soulsoft_serialization.macros.* +import soulsoft_web_http.* +import soulsoft_web_hosting.* +import soulsoft_web_mvc.* +import soulsoft_web_mvc.applicationModels.* +import soulsoft_web_mvc.routing.* +import soulsoft_web_cors.* +import soulsoft_web_routing.* +import soulsoft_extensions_injection.* +import soulsoft_extensions_logging.* +import simapi.authsdk.* +import simapi.communications.* +import simapi.configurations.* +import simapi.controllers.* +import simapi.helpers.* +import simapi.logger.* +import simapi.middlewares.* + +/** + * SimApi 扩展入口(对齐 C# 根命名空间 SimApi 的静态类 SimApiExtensions)。 + */ +public class SimApiExtensions { + private init() {} + + /** + * 注册 SimApi 服务到 WebHostBuilder(自动 addRouting + addControllers + 扫描控制器)。 + * @param builder 主机构建器。 + * @param configure 配置回调。 + * @return 当前构建器。 + */ + public static func addSimApi(builder: WebHostBuilder, configure: (SimApiOptions) -> Unit): WebHostBuilder { + // 自动注册路由(对齐 builder.Services.AddRouting()) + builder.services.addRouting() + // 先构造配置,供后续按开关注册服务(对齐 C# AddSimApi 中先读 options 再注册) + let options = SimApiOptions() + configure(options) + // 响应封装(对齐 C# SimApiResponseFilter,受 EnableSimApiResponseFilter 开关控制): + // 启用时注册自定义 IRequestDelegateFactory 自动封装响应。 + // 必须在 addControllers 之前:soulsoft 用 tryAddSingleton 注册,先到先得,不会被覆盖。 + // 未启用时使用 soulsoft 默认派发(String→ContentResult / ISerializable→ObjectResult / 其余→204)。 + if (options.enableSimApiResponseFilter) { + builder.services.addSingleton() + } + // 自动注册 MVC + 控制器(对齐 builder.Services.AddControllers()) + // 自动扫描调用者包中的 Controller 子类(对齐 C# 的 Assembly.GetTypes() 扫描) + let controllers = SimApiControllerScanner.scan() + addControllers(builder.services, controllers) + // 注册 SimApi 服务 + addSimApiCore(builder, options) + return builder + } + + /** + * 注册 SimApi 服务(默认配置,自动扫描控制器)。 + * @param builder 主机构建器。 + * @return 当前构建器。 + */ + public static func addSimApi(builder: WebHostBuilder): WebHostBuilder { + addSimApi(builder, {_ =>}) + } + + /** + * 应用 SimApi 中间件与内置路由(日志输出对齐 C# UseSimApi)。 + * @param host 构建完成的主机。 + */ + public static func useSimApi(host: WebHost): Unit { + let options = host.services.getOrThrow() + let loggerFactory = host.services.getOrThrow() + // 对齐 C# ILogger:分类名为 SimApiOptions 的全限定名 + let logger = loggerFactory.createLogger() + + // ===== 基础信息(对齐 C# UseSimApi(IHost) 开头) ===== + let now = DateTime.now() + logger.info("当前时区: ${now.zoneId}") + logger.info("主应用版本: ${SimApiUtil.appVersion}\nSimApi版本: ${SimApiUtil.simApiVersion}") + + // RedisCache + if (!options.redisConfiguration.isEmpty()) { + logger.info("开始配置 RedisCache ...") + } + + // SimApiCache + if (options.enableSimApiCache) { + logger.info("开始配置 SimApiCache...") + } + + // SimApiStorage(已实现:addSimApi 中注册 Scoped,桶初始化惰性执行; + // 对齐 C# 的 GetService 预热,但 scoped 服务不能从根解析,故仅输出配置日志) + if (options.enableSimApiStorage) { + logger.info("开始配置 SimApiStorage...") + } + + // SimApiHttpClient + if (options.enableSimApiHttpClient) { + logger.info( + "开始配置 SimApiHttpClient...\n服务器地址: ${options.simApiHttpClientOptions.server}\nAppId: ${options.simApiHttpClientOptions.appId}\nAppkey: ${options.simApiHttpClientOptions.appKey}") + } + + // Synapse(占位) + if (options.enableSynapse) { + logger.info("开始配置 SimApiSynapse...") + } + + // SimApiJob(占位) + if (options.enableJob) { + logger.info("开始配置 SimApiJob ...") + } + + // ===== 中间件与路由(对齐 C# UseSimApi(WebApplication) 的挂载顺序) ===== + // C# 挂载顺序(先挂载 = 外层):ForwardedHeaders(L419) → CORS(L425) → AuthGate(L454) → Auth(L462) + // → 内置路由(L465-496) → Swagger(L502) → RequestLog(L519) → Exception(L525) → LowerUrl → Job + + // ForwardedHeaders(占位:soulsoft 暂无内置,对齐 C# 最先挂载) + if (options.enableForwardHeaders) { + logger.info("开始配置ForwardedHeaders...") + } + + // CORS(对齐 C# builder.UseCors("any")) + if (options.enableCors) { + logger.info("开始配置 Cors全部允许...") + host.useCors() + } + + // AuthGate(对齐 C# UseMiddleware) + if (options.enableSimApiAuthGate) { + logger.info("开始配置 SimApiAuthGate...") + host.use() + } + + // 认证中间件(对齐 C# builder.UseMiddleware()) + if (options.enableSimApiAuth) { + logger.info("开始配置 SimApiAuth...") + host.use() + } + + // 内置路由:RouteOptions 自定义路径时真实注册(默认路径已由内置控制器特性路由覆盖, + // 对齐 C# MapControllerRoute 语义;soulsoft 无约定路由 defaults,用 mapGet/mapPost 委托实现) + let routeOptions = options.simApiRouteOptions + if (let Some(route) <- routeOptions.userInfoRoute) { + if (route != "/user/info") { + host.mapPost(route, { context => + let controller = ActivatorUtilities.createInstance(context.services, + TypeInfo.of()) + if (let c: SimApiBaseController <- controller) { + c.bindRequestContext(context) + } + if (let c: SimApiCommonController <- controller) { + SimApiResultWriter.write(context, c.userInfo()) + } + }) + } + logger.info("注册内置Route: UserInfo => ${route}") + } + if (let Some(route) <- routeOptions.logoutRoute) { + if (route != "/auth/logout") { + host.mapPost(route, { context => + let controller = ActivatorUtilities.createInstance(context.services, + TypeInfo.of()) + if (let c: SimApiBaseController <- controller) { + c.bindRequestContext(context) + } + if (let c: SimApiAuthController <- controller) { + SimApiResultWriter.write(context, c.logout()) + } + }) + } + logger.info("注册内置Route: Logout => ${route}") + } + if (let Some(route) <- routeOptions.webConfigRoute) { + if (route != "/config") { + host.mapGet(route, { context => + let controller = ActivatorUtilities.createInstance(context.services, + TypeInfo.of()) + if (let c: SimApiBaseController <- controller) { + c.bindRequestContext(context) + } + if (let c: SimApiCommonController <- controller) { + SimApiResultWriter.write(context, c.webConfig()) + } + }) + host.mapPost(route, { context => + let controller = ActivatorUtilities.createInstance(context.services, + TypeInfo.of()) + if (let c: SimApiBaseController <- controller) { + c.bindRequestContext(context) + } + if (let c: SimApiCommonController <- controller) { + SimApiResultWriter.write(context, c.webConfigPost()) + } + }) + } + logger.info("注册内置Route: WebConfig => ${route}") + } + + // SimApiDoc(占位,对齐 C# UseSwagger/UseSwaggerUI) + if (options.enableSimApiDoc) { + logger.info("开始配置 SimApiDoc...") + } + + // 请求日志中间件(对齐 C# builder.UseMiddleware()) + if (options.enableRequestLog) { + logger.info("开始配置 SimApiRequestLog...") + host.use() + } + + // 异常中间件最后挂载(最内层,对齐 C# builder.UseMiddleware()) + if (options.enableSimApiException) { + logger.info("开始配置 SimApiException...") + host.use() + } + + // URL 小写 + if (options.enableLowerUrl) { + logger.info("开始配置使用URL小写...") + } + + // SimApiJob Web 控制台(占位) + if (options.enableJob && options.simApiJobOptions.dashboardUrl != None) { + logger.info("开始配置 SimApiJob Web控制台...") + } + + // 响应封装(已实现:addSimApi 中按开关注册 SimApiRequestDelegateFactory 自动封装, + // 对齐 C# SimApiResponseFilter;此处仅输出配置日志) + if (options.enableSimApiResponseFilter) { + logger.info("开始配置 SimApiResponseFilter...") + } + + // 映射控制器端点(对齐 C# UseSimApi 中的 MapControllers) + let callSiteFactory = host.services.getOrThrow() + if (callSiteFactory.isService()) { + host.mapControllers() + } + } + + // ===== 私有辅助 ===== + + private static func addSimApiCore(builder: WebHostBuilder, options: SimApiOptions): WebHostBuilder { + // 注册单例配置(对齐 C# builder.AddSingleton(simApiOptions)) + builder.services.addSingleton(options) + // 子配置不单独注册:中间件统一注入 SimApiOptions 后访问其属性 + // (对齐 C# SimApiExceptionMiddleware(..., SimApiOptions simApiOptions) 风格) + + // 自定义日志格式(替换默认 console provider) + if (options.enableLogger) { + builder.services.addLogging { + logging => + logging.clearProviders() + logging.addProvider(SimApiLoggerProvider()) + } + } + + // 中间件无需注册:挂载时由 ActivatorUtilities 从 DI 解析构造参数创建 + // (对齐 C# builder.UseMiddleware(),其中间件由 UseMiddleware 创建) + + // 认证(DI 自动注入 SimApiOptions) + if (options.enableSimApiAuth) { + builder.services.addSingleton() + } + + // ISimApiAuthChecker 自动扫描注册(对齐 C# AddSimApi 中遍历调用者程序集 AddScoped 注册 checker) + // 扫描调用者包中的 ISimApiAuthChecker 实现类:填充 options.authCheckers(供 @SimApiAuth 执行时解析) + // 并注册到 DI(按实现类类型注册,供 getOrThrow(checkerType) 解析) + if (options.enableSimApiAuth) { + let checkers = SimApiControllerScanner.scanAuthCheckers() + for (checkerType in checkers) { + options.authCheckers.add(checkerType) + builder.services.addScoped(checkerType) + } + } + + // 缓存(DI 自动注入 SimApiOptions) + if (options.enableSimApiCache) { + builder.services.addSingleton() + } + + // HTTP 客户端(DI 自动注入 SimApiOptions) + if (options.enableSimApiHttpClient) { + builder.services.addSingleton() + } + + // 存储(S3/MinIO,对齐 C# AddHttpContextAccessor + AddSingleton; + // 仓颉版注册为 Scoped 以便注入 IHttpContextAccessor(DI 禁止 singleton 消费 scoped), + // 桶初始化由静态守卫保证只执行一次) + if (options.enableSimApiStorage) { + builder.services.addHttpContextAccessor() + builder.services.addScoped() + } + + // AuthGate 认证中心 SDK(对齐 C# 注册 SimApiAuthClient/Center/Iam 单例) + if (options.enableSimApiAuthGate) { + builder.services.addSingleton() + builder.services.addSingleton() + builder.services.addSingleton() + } + + // CORS(对齐 C# builder.Services.AddCors(policy => policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader())) + if (options.enableCors) { + builder.services.addCors { + cors => + cors.addDefaultPolicy { + policy => + policy.allowAnyOrigin() + policy.allowAnyMethod() + policy.allowAnyHeader() + } + } + } + + return builder + } + + /// 注册 MVC 服务,并注册 SimApi 内置控制器 + 用户控制器到 ApplicationPartManager(对齐 .NET AddControllers())。 + private static func addControllers(services: ServiceCollection, controllerTypes: Array): MvcBuilder { + // 调用 soulsoft_web_mvc 的无参 addControllers() 注册 MVC 核心服务 + let mvc = services.addControllers() + let types = ArrayList() + // SimApi 内置控制器 + types.add(TypeInfo.of()) + types.add(TypeInfo.of()) + // 用户控制器 + for (t in controllerTypes) { + types.add(t) + } + let part = AssemblyPart("simapi.controllers", types.toArray()) + mvc.addApplicationPart(part) + mvc + } +} diff --git a/src/extensions/SimApiExtensions.cj b/src/extensions/SimApiExtensions.cj deleted file mode 100644 index b48a160..0000000 --- a/src/extensions/SimApiExtensions.cj +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright (c) 2025 SimcuTeam. All rights reserved. - * 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。 - */ - -package simapi.extensions - -import std.collection.* -import std.convert.* -import std.reflect.* -import std.time.* -import soulsoft_serialization.* -import soulsoft_serialization.macros.* -import soulsoft_web_http.* -import soulsoft_web_hosting.* -import soulsoft_web_mvc.* -import soulsoft_web_mvc.applicationModels.* -import soulsoft_web_mvc.routing.* -import soulsoft_web_cors.* -import soulsoft_web_routing.* -import soulsoft_extensions_injection.* -import soulsoft_extensions_logging.* -import simapi.authsdk.* -import simapi.communications.* -import simapi.configurations.* -import simapi.controllers.* -import simapi.helpers.* -import simapi.logger.* -import simapi.middlewares.* - -/** - * SimApi 扩展入口:对应 C# 的 SimApiExtensions(AddSimApi + UseSimApi)。 - * - * 使用方式(仓颉版,与 addLogging 风格一致): - * ``` - * let builder = WebHost.createBuilder(args) - * builder.services.addLogging() - * builder.addSimApi { options => - * options.enableSimApiAuth = true - * } - * let host = builder.build() - * host.useSimApi() - * host.run() - * ``` - */ -public interface SimApiBuilderExtensions { - /** - * 注册 SimApi 服务到 WebHostBuilder(自动 addRouting + addControllers + 扫描控制器)。 - * @param configure 配置回调。 - * @return 当前构建器。 - */ - func addSimApi(configure: (SimApiOptions) -> Unit): WebHostBuilder - - /** - * 注册 SimApi 服务(默认配置,自动扫描控制器)。 - * @return 当前构建器。 - */ - func addSimApi(): WebHostBuilder -} - -extend WebHostBuilder <: SimApiBuilderExtensions { - /** - * 注册 SimApi 服务(自动 addRouting + addControllers + addLogging + 扫描控制器)。 - * @param configure 配置回调。 - */ - public func addSimApi(configure: (SimApiOptions) -> Unit): WebHostBuilder { - // 自动注册路由(对齐 builder.Services.AddRouting()) - this.services.addRouting() - // 先构造配置,供后续按开关注册服务(对齐 C# AddSimApi 中先读 options 再注册) - let options = SimApiOptions() - configure(options) - // 响应封装(对齐 C# SimApiResponseFilter,受 EnableSimApiResponseFilter 开关控制): - // 启用时注册自定义 IRequestDelegateFactory 自动封装响应。 - // 必须在 addControllers 之前:soulsoft 用 tryAddSingleton 注册,先到先得,不会被覆盖。 - // 未启用时使用 soulsoft 默认派发(String→ContentResult / ISerializable→ObjectResult / 其余→204)。 - if (options.enableSimApiResponseFilter) { - this.services.addSingleton() - } - // 自动注册 MVC + 控制器(对齐 builder.Services.AddControllers()) - // 自动扫描调用者包中的 Controller 子类(对齐 C# 的 Assembly.GetTypes() 扫描) - let controllers = SimApiControllerScanner.scan() - this.services.addControllers(controllers) - // 注册 SimApi 服务 - addSimApiCore(this, options) - } - - /** - * 注册 SimApi 服务(默认配置,自动扫描控制器)。 - */ - public func addSimApi(): WebHostBuilder { - addSimApi({_ =>}) - } -} - -private func addSimApiCore(builder: WebHostBuilder, options: SimApiOptions): WebHostBuilder { - // 注册单例配置(对齐 C# builder.AddSingleton(simApiOptions)) - builder.services.addSingleton(options) - // 子配置不单独注册:中间件统一注入 SimApiOptions 后访问其属性 - // (对齐 C# SimApiExceptionMiddleware(..., SimApiOptions simApiOptions) 风格) - - // 自定义日志格式(替换默认 console provider) - if (options.enableLogger) { - builder.services.addLogging { - logging => - logging.clearProviders() - logging.addProvider(SimApiLoggerProvider()) - } - } - - // 中间件无需注册:挂载时由 ActivatorUtilities 从 DI 解析构造参数创建 - // (对齐 C# builder.UseMiddleware(),其中间件由 UseMiddleware 创建) - - // 认证(DI 自动注入 SimApiOptions) - if (options.enableSimApiAuth) { - builder.services.addSingleton() - } - - // ISimApiAuthChecker 自动扫描注册(对齐 C# AddSimApi 中遍历调用者程序集 AddScoped 注册 checker) - // 扫描调用者包中的 ISimApiAuthChecker 实现类:填充 options.authCheckers(供 @SimApiAuth 执行时解析) - // 并注册到 DI(按实现类类型注册,供 getOrThrow(checkerType) 解析) - if (options.enableSimApiAuth) { - let checkers = SimApiControllerScanner.scanAuthCheckers() - for (checkerType in checkers) { - options.authCheckers.add(checkerType) - builder.services.addScoped(checkerType) - } - } - - // 缓存(DI 自动注入 SimApiOptions) - if (options.enableSimApiCache) { - builder.services.addSingleton() - } - - // HTTP 客户端(DI 自动注入 SimApiOptions) - if (options.enableSimApiHttpClient) { - builder.services.addSingleton() - } - - // 存储(S3/MinIO,对齐 C# AddHttpContextAccessor + AddSingleton; - // 仓颉版注册为 Scoped 以便注入 IHttpContextAccessor(DI 禁止 singleton 消费 scoped), - // 桶初始化由静态守卫保证只执行一次) - if (options.enableSimApiStorage) { - builder.services.addHttpContextAccessor() - builder.services.addScoped() - } - - // AuthGate 认证中心 SDK(对齐 C# 注册 SimApiAuthClient/Center/Iam 单例) - if (options.enableSimApiAuthGate) { - builder.services.addSingleton() - builder.services.addSingleton() - builder.services.addSingleton() - } - - // CORS(对齐 C# builder.Services.AddCors(policy => policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader())) - if (options.enableCors) { - builder.services.addCors { - cors => - cors.addDefaultPolicy { - policy => - policy.allowAnyOrigin() - policy.allowAnyMethod() - policy.allowAnyHeader() - } - } - } - - return builder -} - -/** - * SimApi 主机扩展。 - */ -public interface SimApiHostExtensions { - /** - * 应用 SimApi 中间件与内置路由到 WebHost。 - */ - func useSimApi(): Unit -} - -extend WebHost <: SimApiHostExtensions { - /** - * 应用 SimApi 中间件与内置路由(日志输出对齐 C# UseSimApi)。 - */ - public func useSimApi(): Unit { - let options = this.services.getOrThrow() - let loggerFactory = this.services.getOrThrow() - // 对齐 C# ILogger:分类名为 SimApiOptions 的全限定名 - let logger = loggerFactory.createLogger() - - // ===== 基础信息(对齐 C# UseSimApi(IHost) 开头) ===== - let now = DateTime.now() - logger.info("当前时区: ${now.zoneId}") - logger.info("主应用版本: ${SimApiUtil.appVersion}\nSimApi版本: ${SimApiUtil.simApiVersion}") - - // RedisCache - if (!options.redisConfiguration.isEmpty()) { - logger.info("开始配置 RedisCache ...") - } - - // SimApiCache - if (options.enableSimApiCache) { - logger.info("开始配置 SimApiCache...") - } - - // SimApiStorage(已实现:addSimApi 中注册 Scoped,桶初始化惰性执行; - // 对齐 C# 的 GetService 预热,但 scoped 服务不能从根解析,故仅输出配置日志) - if (options.enableSimApiStorage) { - logger.info("开始配置 SimApiStorage...") - } - - // SimApiHttpClient - if (options.enableSimApiHttpClient) { - logger.info( - "开始配置 SimApiHttpClient...\n服务器地址: ${options.simApiHttpClientOptions.server}\nAppId: ${options.simApiHttpClientOptions.appId}\nAppkey: ${options.simApiHttpClientOptions.appKey}") - } - - // Synapse(占位) - if (options.enableSynapse) { - logger.info("开始配置 SimApiSynapse...") - } - - // SimApiJob(占位) - if (options.enableJob) { - logger.info("开始配置 SimApiJob ...") - } - - // ===== 中间件与路由(对齐 C# UseSimApi(WebApplication) 的挂载顺序) ===== - // C# 挂载顺序(先挂载 = 外层):ForwardedHeaders(L419) → CORS(L425) → AuthGate(L454) → Auth(L462) - // → 内置路由(L465-496) → Swagger(L502) → RequestLog(L519) → Exception(L525) → LowerUrl → Job - - // ForwardedHeaders(占位:soulsoft 暂无内置,对齐 C# 最先挂载) - if (options.enableForwardHeaders) { - logger.info("开始配置ForwardedHeaders...") - } - - // CORS(对齐 C# builder.UseCors("any")) - if (options.enableCors) { - logger.info("开始配置 Cors全部允许...") - this.useCors() - } - - // AuthGate(对齐 C# UseMiddleware) - if (options.enableSimApiAuthGate) { - logger.info("开始配置 SimApiAuthGate...") - this.use() - } - - // 认证中间件(对齐 C# builder.UseMiddleware()) - if (options.enableSimApiAuth) { - logger.info("开始配置 SimApiAuth...") - this.use() - } - - // 内置路由:RouteOptions 自定义路径时真实注册(默认路径已由内置控制器特性路由覆盖, - // 对齐 C# MapControllerRoute 语义;soulsoft 无约定路由 defaults,用 mapGet/mapPost 委托实现) - let routeOptions = options.simApiRouteOptions - if (let Some(route) <- routeOptions.userInfoRoute) { - if (route != "/user/info") { - this.mapPost(route, { context => - let controller = ActivatorUtilities.createInstance(context.services, - TypeInfo.of()) - if (let c: SimApiBaseController <- controller) { - c.bindRequestContext(context) - } - if (let c: SimApiCommonController <- controller) { - SimApiResultWriter.write(context, c.userInfo()) - } - }) - } - logger.info("注册内置Route: UserInfo => ${route}") - } - if (let Some(route) <- routeOptions.logoutRoute) { - if (route != "/auth/logout") { - this.mapPost(route, { context => - let controller = ActivatorUtilities.createInstance(context.services, - TypeInfo.of()) - if (let c: SimApiBaseController <- controller) { - c.bindRequestContext(context) - } - if (let c: SimApiAuthController <- controller) { - SimApiResultWriter.write(context, c.logout()) - } - }) - } - logger.info("注册内置Route: Logout => ${route}") - } - if (let Some(route) <- routeOptions.webConfigRoute) { - if (route != "/config") { - this.mapGet(route, { context => - let controller = ActivatorUtilities.createInstance(context.services, - TypeInfo.of()) - if (let c: SimApiBaseController <- controller) { - c.bindRequestContext(context) - } - if (let c: SimApiCommonController <- controller) { - SimApiResultWriter.write(context, c.webConfig()) - } - }) - this.mapPost(route, { context => - let controller = ActivatorUtilities.createInstance(context.services, - TypeInfo.of()) - if (let c: SimApiBaseController <- controller) { - c.bindRequestContext(context) - } - if (let c: SimApiCommonController <- controller) { - SimApiResultWriter.write(context, c.webConfigPost()) - } - }) - } - logger.info("注册内置Route: WebConfig => ${route}") - } - - // SimApiDoc(占位,对齐 C# UseSwagger/UseSwaggerUI) - if (options.enableSimApiDoc) { - logger.info("开始配置 SimApiDoc...") - } - - // 请求日志中间件(对齐 C# builder.UseMiddleware()) - if (options.enableRequestLog) { - logger.info("开始配置 SimApiRequestLog...") - this.use() - } - - // 异常中间件最后挂载(最内层,对齐 C# builder.UseMiddleware()) - if (options.enableSimApiException) { - logger.info("开始配置 SimApiException...") - this.use() - } - - // URL 小写 - if (options.enableLowerUrl) { - logger.info("开始配置使用URL小写...") - } - - // SimApiJob Web 控制台(占位) - if (options.enableJob && options.simApiJobOptions.dashboardUrl != None) { - logger.info("开始配置 SimApiJob Web控制台...") - } - - // 响应封装(已实现:addSimApi 中按开关注册 SimApiRequestDelegateFactory 自动封装, - // 对齐 C# SimApiResponseFilter;此处仅输出配置日志) - if (options.enableSimApiResponseFilter) { - logger.info("开始配置 SimApiResponseFilter...") - } - - // 映射控制器端点(对齐 C# UseSimApi 中的 MapControllers) - let callSiteFactory = this.services.getOrThrow() - if (callSiteFactory.isService()) { - this.mapControllers() - } - } -} - -/** - * SimApi MVC 注册扩展:对齐 .NET 的 builder.Services.AddControllers()。 - * 注册 MVC 服务 + SimApi 内置控制器 + 用户控制器。 - * - * 说明:控制器端点映射使用 soulsoft_web_mvc 的 mapControllers()(对齐 .NET MapControllers()), - * 宿主在 WebHost 上直接调用 host.mapControllers() 即可。 - */ -public interface SimApiMvcBuilderExtensions { - /** - * 注册 MVC 服务与控制器。 - * @param controllerTypes 用户控制器类型列表(可选)。 - * @return MVC 构建器。 - */ - func addControllers(controllerTypes: Array): MvcBuilder -} - -extend ServiceCollection <: SimApiMvcBuilderExtensions { - /** - * 注册 MVC 服务,并注册 SimApi 内置控制器 + 用户控制器到 ApplicationPartManager。 - * 对齐 .NET 的 AddControllers()(含控制器发现)。 - * @param controllerTypes 用户控制器类型列表。 - * @return MVC 构建器。 - */ - public func addControllers(controllerTypes: Array): MvcBuilder { - // 调用 soulsoft_web_mvc 的无参 addControllers() 注册 MVC 核心服务 - let mvc = this.addControllers() - let types = ArrayList() - // SimApi 内置控制器 - types.add(TypeInfo.of()) - types.add(TypeInfo.of()) - // 用户控制器 - for (t in controllerTypes) { - types.add(t) - } - let part = AssemblyPart("simapi.controllers", types.toArray()) - mvc.addApplicationPart(part) - mvc - } -}