From c7b8148ff52edc00e8bec313d7104a01c0c16836 Mon Sep 17 00:00:00 2001 From: xRain Date: Sun, 16 Aug 2026 22:46:48 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=8F=90=E5=8F=96=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E5=93=8D=E5=BA=94=E5=B0=81=E8=A3=85=20SimApiResultWri?= =?UTF-8?q?ter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dispatchResult 复用 SimApiResultWriter(IActionResult/SimApiBaseResponse/String/Unit/其他 五分支) - @SimApiAuth 鉴权执行(checkSimApiAuth:401/类型403/checker 遍历) - @OriginResponse 跳过封装原样输出 --- .../SimApiRequestDelegateFactory.cj | 87 +++++++++++++++---- src/extensions/SimApiResultWriter.cj | 42 +++++++++ 2 files changed, 112 insertions(+), 17 deletions(-) create mode 100644 src/extensions/SimApiResultWriter.cj diff --git a/src/extensions/SimApiRequestDelegateFactory.cj b/src/extensions/SimApiRequestDelegateFactory.cj index 74a678e..c3ea676 100644 --- a/src/extensions/SimApiRequestDelegateFactory.cj +++ b/src/extensions/SimApiRequestDelegateFactory.cj @@ -23,8 +23,12 @@ import soulsoft_web_mvc.modelBindings.* import soulsoft_web_mvc.abstractions.* import soulsoft_extensions_options.* import soulsoft_extensions_injection.* +import simapi.attributes.{SimApiAuth, OriginResponse} import simapi.communications.* +import simapi.configurations.* import simapi.controllers.* +import simapi.helpers.{SimApiError} +import simapi.interfaces.* /** * 自定义请求委托工厂:接管 soulsoft 的 ControllerRequestDelegateFactory, @@ -57,6 +61,7 @@ struct SimApiActionInvoker { public func apply(): Unit { let controller = createControllerInstance() + checkSimApiAuth() let modelBindingContext = ActionBindingContext(context, actionDescriptor.actionFunction.parameters) let boundParameters = modelBinder.bind(modelBindingContext) if (!modelBindingContext.modelState.isValid) { @@ -67,6 +72,52 @@ struct SimApiActionInvoker { } } + /// 检查 @SimApiAuth 注解并执行鉴权(对齐 C# SimApiAuthAttribute.OnActionExecuting): + /// 未登录 401 → 类型权限 403 → 遍历执行 ISimApiAuthChecker + private func checkSimApiAuth() { + var auth: ?SimApiAuth = None + for (item in actionDescriptor.endpointMetadata) { + if (let a: SimApiAuth <- item) { + auth = Some(a) + break + } + } + if (let Some(auth) <- auth) { + // 1. 未登录 → 401 + var loginItem: SimApiLoginItem = SimApiLoginItem("") + match (context.items.get("LoginInfo")) { + case Some(v) => + if (let l: SimApiLoginItem <- v) { + loginItem = l + } else { + SimApiError.error(code: 401, message: "需要登录") + } + case None => + SimApiError.error(code: 401, message: "需要登录") + } + + // 2. 类型权限校验 → 403 + if (!auth.`type`.isEmpty()) { + if (!loginItem._types.contains(auth.`type`)) { + SimApiError.error(code: 403, message: "无权访问") + } + } + + // 3. 遍历执行 ISimApiAuthChecker(从 DI 按注册类型解析) + let token = match (context.items.get("LoginToken")) { + case Some(v) => if (let s: String <- v) { s } else { "" } + case None => "" + } + let options = context.services.getOrThrow() + for (checkerType in options.authCheckers) { + let instance = context.services.getOrThrow(checkerType) + if (let checker: ISimApiAuthChecker <- instance) { + checker.run(loginItem, token) + } + } + } + } + /// 模型绑定失败时写入 ProblemDetails 响应 private func handleInvalidModelState(modelBindingContext: ActionBindingContext) { let options = context.services.getOrThrow>() @@ -84,24 +135,26 @@ struct SimApiActionInvoker { /// 结果派发 + 自动封装(对齐 C# SimApiResponseFilter) private func dispatchResult(actionResult: Any) { - if (let result: IActionResult <- actionResult) { - // 显式返回 IActionResult(如 ContentResult)→ 原样 - result.invoke(context) - } else if (let result: SimApiBaseResponse <- actionResult) { - // 已是 SimApiBaseResponse(含子类)→ 原样输出 - ObjectResult(result).invoke(context) - } else if (let result: String <- actionResult) { - // String → SimApiResponse(data 为字符串) - ObjectResult(SimApiResponse(result)).invoke(context) - } else if (let result: Unit <- actionResult) { - // void/无返回 → SimApiBaseResponse()({code:200, message:成功}) - context.response.writeAsJson(SimApiBaseResponse()) - } else { - // 其他对象(DTO/数组/动态结构)→ SimApiDataResponse - // data 由 SimApiDataResponse.serializeObject 内嵌为对象(ISerializable → 对象; - // HashMap 等动态结构 → SimApiJson 序列化后解析内嵌),不会变成 JSON 字符串 - ObjectResult(SimApiDataResponse(actionResult)).invoke(context) + // @OriginResponse:跳过统一封装,原样输出(对齐 C# OnResultExecuting 遇注解直接 return) + var originResponse = false + for (item in actionDescriptor.endpointMetadata) { + if (item is OriginResponse) { + originResponse = true + break + } } + if (originResponse) { + if (let s: String <- actionResult) { + // String 原样输出文本(对齐 C# string 返回直接写入) + context.response.contentType = "application/json; charset=utf-8" + context.response.write(s) + } else { + ObjectResult(actionResult).invoke(context) + } + return + } + // 统一封装(IActionResult/SimApiBaseResponse/String/Unit/其他对象) + SimApiResultWriter.write(context, actionResult) } /// 根据 ModelState 错误构建 ValidationProblemDetails diff --git a/src/extensions/SimApiResultWriter.cj b/src/extensions/SimApiResultWriter.cj new file mode 100644 index 0000000..9416a3a --- /dev/null +++ b/src/extensions/SimApiResultWriter.cj @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 SimcuTeam. All rights reserved. + * 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。 + */ + +package simapi.extensions + +import soulsoft_web_http.* +import soulsoft_web_mvc.core.* +import simapi.communications.* + +/** + * 统一响应封装工具(对齐 C# SimApiResponseFilter 的包装分支)。 + * 供 SimApiRequestDelegateFactory 与内置路由委托复用: + * - SimApiBaseResponse(含子类)→ 原样输出 + * - String → SimApiResponse(data 为字符串) + * - Unit(void)→ SimApiBaseResponse()({code:200, message:成功}) + * - 其他对象(DTO/数组/动态结构)→ SimApiDataResponse(data 内嵌为对象) + */ +public class SimApiResultWriter { + private init() {} + + public static func write(context: HttpContext, actionResult: Any): Unit { + if (let result: IActionResult <- actionResult) { + // 显式返回 IActionResult(如 ContentResult)→ 原样 + result.invoke(context) + } else if (let result: SimApiBaseResponse <- actionResult) { + // 已是 SimApiBaseResponse(含子类)→ 原样输出 + ObjectResult(result).invoke(context) + } else if (let result: String <- actionResult) { + // String → SimApiResponse(data 为字符串) + ObjectResult(SimApiResponse(result)).invoke(context) + } else if (let result: Unit <- actionResult) { + // void/无返回 → SimApiBaseResponse()({code:200, message:成功}) + context.response.writeAsJson(SimApiBaseResponse()) + } else { + // 其他对象(DTO/数组/动态结构)→ SimApiDataResponse + // data 由 SimApiDataResponse.serializeObject 内嵌为对象 + ObjectResult(SimApiDataResponse(actionResult)).invoke(context) + } + } +}