refactor: 提取统一响应封装 SimApiResultWriter
- dispatchResult 复用 SimApiResultWriter(IActionResult/SimApiBaseResponse/String/Unit/其他 五分支) - @SimApiAuth 鉴权执行(checkSimApiAuth:401/类型403/checker 遍历) - @OriginResponse 跳过封装原样输出
This commit is contained in:
@@ -23,8 +23,12 @@ import soulsoft_web_mvc.modelBindings.*
|
|||||||
import soulsoft_web_mvc.abstractions.*
|
import soulsoft_web_mvc.abstractions.*
|
||||||
import soulsoft_extensions_options.*
|
import soulsoft_extensions_options.*
|
||||||
import soulsoft_extensions_injection.*
|
import soulsoft_extensions_injection.*
|
||||||
|
import simapi.attributes.{SimApiAuth, OriginResponse}
|
||||||
import simapi.communications.*
|
import simapi.communications.*
|
||||||
|
import simapi.configurations.*
|
||||||
import simapi.controllers.*
|
import simapi.controllers.*
|
||||||
|
import simapi.helpers.{SimApiError}
|
||||||
|
import simapi.interfaces.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义请求委托工厂:接管 soulsoft 的 ControllerRequestDelegateFactory,
|
* 自定义请求委托工厂:接管 soulsoft 的 ControllerRequestDelegateFactory,
|
||||||
@@ -57,6 +61,7 @@ struct SimApiActionInvoker {
|
|||||||
|
|
||||||
public func apply(): Unit {
|
public func apply(): Unit {
|
||||||
let controller = createControllerInstance()
|
let controller = createControllerInstance()
|
||||||
|
checkSimApiAuth()
|
||||||
let modelBindingContext = ActionBindingContext(context, actionDescriptor.actionFunction.parameters)
|
let modelBindingContext = ActionBindingContext(context, actionDescriptor.actionFunction.parameters)
|
||||||
let boundParameters = modelBinder.bind(modelBindingContext)
|
let boundParameters = modelBinder.bind(modelBindingContext)
|
||||||
if (!modelBindingContext.modelState.isValid) {
|
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<SimApiOptions>()
|
||||||
|
for (checkerType in options.authCheckers) {
|
||||||
|
let instance = context.services.getOrThrow(checkerType)
|
||||||
|
if (let checker: ISimApiAuthChecker <- instance) {
|
||||||
|
checker.run(loginItem, token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 模型绑定失败时写入 ProblemDetails 响应
|
/// 模型绑定失败时写入 ProblemDetails 响应
|
||||||
private func handleInvalidModelState(modelBindingContext: ActionBindingContext) {
|
private func handleInvalidModelState(modelBindingContext: ActionBindingContext) {
|
||||||
let options = context.services.getOrThrow<IOptions<ApiBehaviorOptions>>()
|
let options = context.services.getOrThrow<IOptions<ApiBehaviorOptions>>()
|
||||||
@@ -84,24 +135,26 @@ struct SimApiActionInvoker {
|
|||||||
|
|
||||||
/// 结果派发 + 自动封装(对齐 C# SimApiResponseFilter)
|
/// 结果派发 + 自动封装(对齐 C# SimApiResponseFilter)
|
||||||
private func dispatchResult(actionResult: Any) {
|
private func dispatchResult(actionResult: Any) {
|
||||||
if (let result: IActionResult <- actionResult) {
|
// @OriginResponse:跳过统一封装,原样输出(对齐 C# OnResultExecuting 遇注解直接 return)
|
||||||
// 显式返回 IActionResult(如 ContentResult)→ 原样
|
var originResponse = false
|
||||||
result.invoke(context)
|
for (item in actionDescriptor.endpointMetadata) {
|
||||||
} else if (let result: SimApiBaseResponse <- actionResult) {
|
if (item is OriginResponse) {
|
||||||
// 已是 SimApiBaseResponse(含子类)→ 原样输出
|
originResponse = true
|
||||||
ObjectResult<Any>(result).invoke(context)
|
break
|
||||||
} else if (let result: String <- actionResult) {
|
}
|
||||||
// String → SimApiResponse<String>(data 为字符串)
|
|
||||||
ObjectResult<Any>(SimApiResponse<String>(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<String,Any> 等动态结构 → SimApiJson 序列化后解析内嵌),不会变成 JSON 字符串
|
|
||||||
ObjectResult<Any>(SimApiDataResponse(actionResult)).invoke(context)
|
|
||||||
}
|
}
|
||||||
|
if (originResponse) {
|
||||||
|
if (let s: String <- actionResult) {
|
||||||
|
// String 原样输出文本(对齐 C# string 返回直接写入)
|
||||||
|
context.response.contentType = "application/json; charset=utf-8"
|
||||||
|
context.response.write(s)
|
||||||
|
} else {
|
||||||
|
ObjectResult<Any>(actionResult).invoke(context)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 统一封装(IActionResult/SimApiBaseResponse/String/Unit/其他对象)
|
||||||
|
SimApiResultWriter.write(context, actionResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 根据 ModelState 错误构建 ValidationProblemDetails
|
/// 根据 ModelState 错误构建 ValidationProblemDetails
|
||||||
|
|||||||
@@ -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<String>(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<Any>(result).invoke(context)
|
||||||
|
} else if (let result: String <- actionResult) {
|
||||||
|
// String → SimApiResponse<String>(data 为字符串)
|
||||||
|
ObjectResult<Any>(SimApiResponse<String>(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<Any>(SimApiDataResponse(actionResult)).invoke(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user