refactor: DTO 字段去下划线 + SimApiBaseResponse 不再可继承 + 删除 SimApiJson/toJsonString

- 纯 POCO DTO 字段去下划线:AuthDto(id/name/applicationId/profileId/scene 等)、
  SimApiLoginItem(id/types/meta/extra)、AesBodyRequest(data)、
  SimApiBaseResponse 系列(code/message/data/list/page/count/total)
- SimApiBaseResponse 去 open(不再允许继承)+ 删 @SerializerParent(无子类继承)
- SimApiResponse<T> / SimApiDataResponse 改为组合(自带 code/message/data)
- 删除 toJsonString(无使用点,统一走 simapi_serialization)
- 删除 SimApiJson.cj:json() → SimApiUtil.json() 直接序列化;
  escapeJson() 迁入 SimApiUtil
- 同步更新所有字段引用(AuthCenter/AuthIam/HttpClient/ExceptionMiddleware 等)
- 保留 sqlsharp 实体 User/Bot 的 _ 前缀(soulsoft 宏依赖)
This commit is contained in:
2026-08-18 01:15:15 +08:00
parent ea31bd9b6a
commit c7099040fa
15 changed files with 175 additions and 217 deletions
+10 -10
View File
@@ -2,19 +2,19 @@ version = 0
[requires]
soulsoft_extensions_hosting = {version = "1.0.20260528"}
soulsoft_web_http = {version = "1.0.20260528"}
soulsoft_web_hosting = {version = "1.0.20260528"}
soulsoft_extensions_logging = {version = "1.0.20260528"}
soulsoft_web_cors = {version = "1.0.20260528"}
soulsoft_extensions_options_configuration = {version = "1.0.20260528"}
soulsoft_web_http = {version = "1.0.20260528"}
soulsoft_extensions_logging = {version = "1.0.20260528"}
soulsoft_extensions_configuration = {version = "1.0.20260528"}
soulsoft_web_routing = {version = "1.0.20260528"}
soulsoft_extensions_logging_console = {version = "1.0.20260528"}
soulsoft_serialization = {version = "1.0.20260528"}
soulsoft_net_http = {version = "1.0.20260528"}
soulsoft_web_mvc = {version = "1.0.20260528"}
soulsoft_extensions_injection = {version = "1.0.20260528"}
redis = {version = "1.0.20260627"}
soulsoft_web_hosting = {version = "1.0.20260528"}
soulsoft_extensions_logging_configuration = {version = "1.0.20260528"}
soulsoft_identity_claims = {version = "1.0.20260528"}
soulsoft_extensions_configuration = {version = "1.0.20260528"}
soulsoft_web_mvc = {version = "1.0.20260528"}
soulsoft_serialization = {version = "1.0.20260528"}
soulsoft_extensions_options = {version = "1.0.20260528"}
soulsoft_web_cors = {version = "1.0.20260528"}
redis = {version = "1.0.20260627"}
soulsoft_extensions_injection = {version = "1.0.20260528"}
soulsoft_net_http = {version = "1.0.20260528"}
+17 -17
View File
@@ -65,7 +65,7 @@ public class SimApiAuthCenter {
response.ensureSuccessStatusCode()
let json = response.content.readAsString()
let resp = JsonSerializer.Deserialize<SimApiBaseResponse>(json)
SimApiError.errorWhen(resp._code != 200, code: 400, message: "签名验证失败")
SimApiError.errorWhen(resp.code != 200, code: 400, message: "签名验证失败")
} finally {
response.close()
}
@@ -92,7 +92,7 @@ public class SimApiAuthCenter {
body["keyword"] = keyword
body["skip"] = skip
body["take"] = take
_client.signQuery<Array<AppAndProfileItem>>("/api/auth/group/search", body: SimApiJson.json(Some(body)))
_client.signQuery<Array<AppAndProfileItem>>("/api/auth/group/search", body: SimApiUtil.json(Some(body)))
}
/**
@@ -102,7 +102,7 @@ public class SimApiAuthCenter {
var body = HashMap<String, Any>()
body["profileId"] = profileId
body["groupId"] = groupId
_client.signQuery<GroupDetailTreeNode>("/api/auth/group/detail", body: SimApiJson.json(Some(body)))
_client.signQuery<GroupDetailTreeNode>("/api/auth/group/detail", body: SimApiUtil.json(Some(body)))
}
/**
@@ -113,7 +113,7 @@ public class SimApiAuthCenter {
body["groupId"] = groupId
body["profileId"] = profileId
_client.signQuery<Array<String>>("/api/auth/internal/group/related-group-ids",
body: SimApiJson.json(Some(body)))
body: SimApiUtil.json(Some(body)))
}
// ===== Profile 相关 =====
@@ -126,7 +126,7 @@ public class SimApiAuthCenter {
body["keyword"] = keyword
body["skip"] = skip
body["take"] = take
_client.signQuery<Array<AppAndProfileItem>>("/api/auth/profile/search", body: SimApiJson.json(Some(body)))
_client.signQuery<Array<AppAndProfileItem>>("/api/auth/profile/search", body: SimApiUtil.json(Some(body)))
}
/**
@@ -139,7 +139,7 @@ public class SimApiAuthCenter {
arr.add(id)
}
body["ids"] = arr.toArray()
_client.signQuery<Array<AppAndProfileItem>>("/api/auth/profile/list", body: SimApiJson.json(Some(body)))
_client.signQuery<Array<AppAndProfileItem>>("/api/auth/profile/list", body: SimApiUtil.json(Some(body)))
}
// ===== AuthGate 内部应用专用 =====
@@ -151,7 +151,7 @@ public class SimApiAuthCenter {
var body = HashMap<String, Any>()
body["ProfileId"] = profileId
body["AppId"] = applicationId
_client.signQuery<Bool>("/api/auth/internal/app/check-owner", body: SimApiJson.json(Some(body)))
_client.signQuery<Bool>("/api/auth/internal/app/check-owner", body: SimApiUtil.json(Some(body)))
}
/**
@@ -166,7 +166,7 @@ public class SimApiAuthCenter {
}
body["AllowedAppIds"] = arr.toArray()
_client.signQuery<Array<AppAndProfileItem>>("/api/auth/internal/app/related",
body: SimApiJson.json(Some(body)))
body: SimApiUtil.json(Some(body)))
}
// ===== 系统登录 =====
@@ -184,7 +184,7 @@ public class SimApiAuthCenter {
if (let Some(scene) <- scene) { body["scene"] = scene }
if (let Some(data) <- data) { body["data"] = data }
if (let Some(backUrl) <- backUrl) { body["backUrl"] = backUrl }
let code = _client.signQuery<String>("/api/auth/login/code", body: SimApiJson.json(Some(body)))
let code = _client.signQuery<String>("/api/auth/login/code", body: SimApiUtil.json(Some(body)))
let server = _client.server
GetCodeResponse(code, server, "${server}/auth?code=${code}")
}
@@ -195,10 +195,10 @@ public class SimApiAuthCenter {
public func getLoginInfo(code: String, scene!: ?String = None): LoginInfoResponse {
var body = HashMap<String, Any>()
body["code"] = code
// 说明:C# 的 ErrorWhenNull(resp, 400232, "登录信息获取失败") 对应 signQuery 内部 _data.getOrThrow() 的
// 说明:C# 的 ErrorWhenNull(resp, 400232, "登录信息获取失败") 对应 signQuery 内部 data.getOrThrow() 的
// None 分支;仓颉版 signQuery 返回非空 T(data 缺失即抛异常),故此处无需重复判空。
let resp = _client.signQuery<LoginInfoResponse>("/api/auth/login/get", body: SimApiJson.json(Some(body)))
SimApiError.errorWhen(resp._scene != scene, code: 403003, message: "登录场景不匹配")
let resp = _client.signQuery<LoginInfoResponse>("/api/auth/login/get", body: SimApiUtil.json(Some(body)))
SimApiError.errorWhen(resp.scene != scene, code: 403003, message: "登录场景不匹配")
resp
}
@@ -214,7 +214,7 @@ public class SimApiAuthCenter {
if (let Some(data) <- data) { body["data"] = data }
if (let Some(backUrl) <- backUrl) { body["backUrl"] = backUrl }
body["profileId"] = userId
let code = _client.signQuery<String>("/api/auth/confirm/code", body: SimApiJson.json(Some(body)))
let code = _client.signQuery<String>("/api/auth/confirm/code", body: SimApiUtil.json(Some(body)))
let server = _client.server
GetCodeResponse(code, server, "${server}/confirm?code=${code}")
}
@@ -225,14 +225,14 @@ public class SimApiAuthCenter {
public func confirm(code: String, scene: String, userId!: ?String = None): ConfirmResponse {
var body = HashMap<String, Any>()
body["code"] = code
let resp = _client.signQuery<ConfirmResponse>("/api/auth/confirm/get", body: SimApiJson.json(Some(body)))
SimApiError.errorWhen(userId != Some(resp._profileId), code: 403002, message: "安全确认身份不匹配")
SimApiError.errorWhen(resp._scene != scene, code: 403003, message: "安全确认场景不匹配")
let resp = _client.signQuery<ConfirmResponse>("/api/auth/confirm/get", body: SimApiUtil.json(Some(body)))
SimApiError.errorWhen(userId != Some(resp.profileId), code: 403002, message: "安全确认身份不匹配")
SimApiError.errorWhen(resp.scene != scene, code: 403003, message: "安全确认场景不匹配")
resp
}
/// 简单单字段请求体:{"field":"value"}
private static func simpleBody(field: String, value: String): String {
"{\"${field}\":\"${SimApiJson.escapeJson(value)}\"}"
"{\"${field}\":\"${SimApiUtil.escapeJson(value)}\"}"
}
}
+42 -42
View File
@@ -18,49 +18,49 @@ import std.collection.*
* 应用/Profile 通用项(对齐 C# AppAndProfileItem)。
*/
public class AppAndProfileItem {
public var _id: String = ""
public var _name: String = ""
public var _image: ?String = None
public var _description: ?String = None
public var id: String = ""
public var name: String = ""
public var image: ?String = None
public var description: ?String = None
}
/**
* 安全确认响应(对齐 C# ConfirmResponse)。
* _data 用 ?HashMap<String, Any> 对齐 C# Dictionary<string,object>?(任意 JSON 对象)。
* data 用 ?HashMap<String, Any> 对齐 C# Dictionary<string,object>?(任意 JSON 对象)。
*/
public class ConfirmResponse {
public var _applicationId: String = ""
public var _profileId: String = ""
public var _scene: ?String = None
public var _data: ?HashMap<String, Any> = None
public var applicationId: String = ""
public var profileId: String = ""
public var scene: ?String = None
public var data: ?HashMap<String, Any> = None
}
/**
* 登录信息响应(对齐 C# LoginInfoResponse)。
*/
public class LoginInfoResponse {
public var _scene: ?String = None
public var _data: ?HashMap<String, Any> = None
public var _profileId: String = ""
public var _name: String = ""
public var _image: ?String = None
public var _description: ?String = None
public var scene: ?String = None
public var data: ?HashMap<String, Any> = None
public var profileId: String = ""
public var name: String = ""
public var image: ?String = None
public var description: ?String = None
}
/**
* 获取授权码响应(对齐 C# GetCodeResponse)。
*/
public class GetCodeResponse {
public var _code: String = ""
public var _server: String = ""
public var _fullUrl: String = ""
public var code: String = ""
public var server: String = ""
public var fullUrl: String = ""
public init() {}
public init(code: String, server: String, fullUrl: String) {
this._code = code
this._server = server
this._fullUrl = fullUrl
this.code = code
this.server = server
this.fullUrl = fullUrl
}
}
@@ -68,42 +68,42 @@ public class GetCodeResponse {
* 群组关联项(对齐 C# GroupRelatedItem)。
*/
public class GroupRelatedItem {
public var _id: String = ""
public var _name: String = ""
public var _image: ?String = None
public var _description: ?String = None
public var _isOwner: Bool = false
public var _isAdmin: Bool = false
public var _isMember: Bool = false
public var id: String = ""
public var name: String = ""
public var image: ?String = None
public var description: ?String = None
public var isOwner: Bool = false
public var isAdmin: Bool = false
public var isMember: Bool = false
}
/**
* 群组详情树节点(对齐 C# GroupDetailTreeNodechildren 递归)。
*/
public class GroupDetailTreeNode {
public var _id: String = ""
public var _name: String = ""
public var _image: ?String = None
public var _description: ?String = None
public var _sort: Int64 = 0
public var _children: Array<GroupDetailTreeNode> = []
public var id: String = ""
public var name: String = ""
public var image: ?String = None
public var description: ?String = None
public var sort: Int64 = 0
public var children: Array<GroupDetailTreeNode> = []
}
/**
* 权限项(对齐 C# PermissionItem)。
*/
public class PermissionItem {
public var _identifier: String = ""
public var _name: String = ""
public var _group: String = ""
public var _description: String = ""
public var identifier: String = ""
public var name: String = ""
public var group: String = ""
public var description: String = ""
public init() {}
public init(identifier: String, name: String, group: String, description: String) {
this._identifier = identifier
this._name = name
this._group = group
this._description = description
this.identifier = identifier
this.name = name
this.group = group
this.description = description
}
}
+7 -7
View File
@@ -29,15 +29,15 @@ public class SimApiAuthIam {
var items = ArrayList<Any>()
for (p in permissions) {
var item = HashMap<String, Any>()
item["identifier"] = p._identifier
item["name"] = p._name
item["group"] = p._group
item["description"] = p._description
item["identifier"] = p.identifier
item["name"] = p.name
item["group"] = p.group
item["description"] = p.description
items.add(item)
}
var body = HashMap<String, Any>()
body["permissions"] = items.toArray()
_client.signQuery<String>("/api/iam/permission/register", body: SimApiJson.json(Some(body)))
_client.signQuery<String>("/api/iam/permission/register", body: SimApiUtil.json(Some(body)))
}
/**
@@ -49,7 +49,7 @@ public class SimApiAuthIam {
if (let Some(groupId) <- groupId) {
body["groupId"] = groupId
}
_client.signQuery<Array<String>>("/api/iam/permission/owned", body: SimApiJson.json(Some(body)))
_client.signQuery<Array<String>>("/api/iam/permission/owned", body: SimApiUtil.json(Some(body)))
}
/**
@@ -62,7 +62,7 @@ public class SimApiAuthIam {
if (let Some(groupId) <- groupId) {
body["groupId"] = groupId
}
let ok = _client.signQuery<Bool>("/api/iam/permission/check", body: SimApiJson.json(Some(body)))
let ok = _client.signQuery<Bool>("/api/iam/permission/check", body: SimApiUtil.json(Some(body)))
SimApiError.errorWhen(!ok, code: 403, message: "没有该权限")
}
}
+30 -47
View File
@@ -6,33 +6,29 @@
package simapi.communications
import std.collection.*
import simapi_serialization.*
import simapi_serialization.macros.*
/**
* 基础响应体:所有接口统一返回该结构。
* HTTP 状态码始终 200,业务错误通过 code 字段表达。
* 序列化/反序列化由 simapi_serialization 反射处理(无需接口/宏)
* @SerializerParent 使子类(SimApiResponse<T>/SimApiDataResponse)序列化时包含 _code/_message。
* 序列化/反序列化由 simapi_serialization 反射处理(无需接口/宏)
*/
@SerializerParent
public open class SimApiBaseResponse {
public var _code: Int64 = 200
public var _message: String = "成功"
public class SimApiBaseResponse {
public var code: Int64 = 200
public var message: String = "成功"
public init(code: Int64, message: String) {
this._code = code
this._message = message
this.code = code
this.message = message
}
public init(code: Int64) {
this._code = code
this._message = getDefaultMessage(code)
this.code = code
this.message = getDefaultMessage(code)
}
public init() {
this._code = 200
this._message = "成功"
this.code = 200
this.message = "成功"
}
/**
@@ -50,20 +46,6 @@ public open class SimApiBaseResponse {
case _ => "未知错误代码"
}
}
/**
* 序列化为 JSON 字符串(可选 data 字段)。
* @param dataJson 已序列化的 data JSON 字符串(可选)。
*/
public func toJsonString(dataJson!: String = ""): String {
var sb = StringBuilder()
sb.append("{\"code\":${_code},\"message\":\"${SimApiJson.escapeJson(_message)}\"")
if (!dataJson.isEmpty()) {
sb.append(",\"data\":${dataJson}")
}
sb.append("}")
sb.toString()
}
}
/**
@@ -72,18 +54,18 @@ public open class SimApiBaseResponse {
* 说明:泛型类暂不通过反射序列化,可手动转换为 SimApiResponse。
*/
public class PageResponse<T> {
public var _list: Array<T> = Array<T>()
public var _page: Int64 = 1
public var _count: Int64 = 20
public var _total: Int64 = 0
public var list: Array<T> = Array<T>()
public var page: Int64 = 1
public var count: Int64 = 20
public var total: Int64 = 0
public init() {}
public init(list: Array<T>, page: Int64, count: Int64, total: Int64) {
this._list = list
this._page = page
this._count = count
this._total = total
this.list = list
this.page = page
this.count = count
this.total = total
}
}
@@ -91,28 +73,29 @@ public class PageResponse<T> {
* 带数据的动态响应。
* @param T 数据类型。
* 序列化/反序列化由 simapi_serialization 反射处理:
* - data 为对象/数组/标量,反射递归
* - code/message/data 为普通字段,反射递归
* - data 支持任意 T(免约束)
* 注:泛型嵌套(SimApiResponse<T>)的反射反序列化已验证可用。
*/
public class SimApiResponse<T> <: SimApiBaseResponse {
public var _data: ?T = None
public class SimApiResponse<T> {
public var code: Int64 = 200
public var message: String = "成功"
public var data: ?T = None
public init() {
super()
}
public init() {}
public init(data: T) {
super()
this._data = Some(data)
this.data = Some(data)
}
public init(code: Int64, message: String) {
super(code, message)
this.code = code
this.message = message
}
public init(code: Int64, message: String, data: T) {
super(code, message)
this._data = Some(data)
this.code = code
this.message = message
this.data = Some(data)
}
}
+12 -11
View File
@@ -12,27 +12,28 @@ package simapi.communications
* SimApiRequestDelegateFactory 派发结果时统一包装 DTO/数组/动态结构。
*
* 序列化/反序列化由 simapi_serialization 反射处理:
* - data 为对象/数组/标量,反射递归
* - code/message/data 为普通字段,反射递归
* - data 为动态结构(HashMap<String, Any>)同样反射支持
*/
public class SimApiDataResponse <: SimApiBaseResponse {
public var _data: ?Any = None
public class SimApiDataResponse {
public var code: Int64 = 200
public var message: String = "成功"
public var data: ?Any = None
public init() {
super()
}
public init() {}
public init(data: Any) {
super()
this._data = Some(data)
this.data = Some(data)
}
public init(code: Int64, message: String) {
super(code, message)
this.code = code
this.message = message
}
public init(code: Int64, message: String, data: Any) {
super(code, message)
this._data = Some(data)
this.code = code
this.message = message
this.data = Some(data)
}
}
-51
View File
@@ -1,51 +0,0 @@
/*
* Copyright (c) 2025 SimcuTeam. All rights reserved.
* 移植自 C# 项目 SimApiE:\simcu\simapi-net),遵循 MIT 许可证。
* JSON 序列化统一工具(对应 C# SimApiUtil.Json)。
*/
package simapi.communications
import std.collection.*
import simapi_serialization.*
/**
* JSON 序列化静态工具类。
*
* 说明:序列化核心委托给 simapi_serializationJsonSerializer.Serialize),
* 全框架 JSON 输出统一走此处,保证与序列化库行为一致。
*/
public class SimApiJson {
private init() {}
/**
* 对象序列化为 JSON 字符串(统一入口)。
* @param obj 任意对象(None 输出 null)。
*/
public static func json(obj: ?Any): String {
if (let Some(obj) <- obj) {
return JsonSerializer.Serialize(obj)
}
"null"
}
/**
* JSON 字符串转义(统一入口)。
* @param s 原始字符串。
* @return 转义后可直接放入 JSON 字符串字面量的内容。
*/
public static func escapeJson(s: String): String {
var sb = StringBuilder()
for (c in s.runes()) {
match (c) {
case '"' => sb.append("\\\"")
case '\\' => sb.append("\\\\")
case '\n' => sb.append("\\n")
case '\r' => sb.append("\\r")
case '\t' => sb.append("\\t")
case _ => sb.append(c)
}
}
sb.toString()
}
}
+13 -13
View File
@@ -13,27 +13,27 @@ import std.collection.*
* 对齐 C# SimApiLoginItemId / Type / Meta / Extra)。
*
* 序列化/反序列化由 simapi_serialization 反射处理(免标注、免约束),
* 字段 _id/_types/_meta/_extra 全部为受支持类型(String/Array/HashMap)。
* 字段 id/types/meta/extra 全部为受支持类型(String/Array/HashMap)。
*/
public class SimApiLoginItem {
public var _id: String = ""
public var _types: Array<String> = ["user"]
public var _meta: HashMap<String, String> = HashMap<String, String>()
public var _extra: HashMap<String, Any> = HashMap<String, Any>()
public var id: String = ""
public var types: Array<String> = ["user"]
public var meta: HashMap<String, String> = HashMap<String, String>()
public var extra: HashMap<String, Any> = HashMap<String, Any>()
public init() {}
public init(id: String) {
this._id = id
this._types = ["user"]
this._meta = HashMap<String, String>()
this._extra = HashMap<String, Any>()
this.id = id
this.types = ["user"]
this.meta = HashMap<String, String>()
this.extra = HashMap<String, Any>()
}
public init(id: String, types: Array<String>) {
this._id = id
this._types = types
this._meta = HashMap<String, String>()
this._extra = HashMap<String, Any>()
this.id = id
this.types = types
this.meta = HashMap<String, String>()
this.extra = HashMap<String, Any>()
}
}
+3 -3
View File
@@ -17,7 +17,7 @@ import simapi.interfaces.*
* AES body 请求({"data": "密文"},对齐 C# SimApiOneFieldRequest<string>)。
*/
public class AesBodyRequest {
public var _data: String = ""
public var data: String = ""
}
/**
@@ -54,7 +54,7 @@ public class SimApiAesBodyChecker {
// 2. 反序列化 {"data": "密文"}
let req = JsonSerializer.Deserialize<AesBodyRequest>(body)
if (req._data.isEmpty()) {
if (req.data.isEmpty()) {
SimApiError.error(code: 400, message: "请求体缺少密文Data字段")
}
@@ -76,7 +76,7 @@ public class SimApiAesBodyChecker {
}
// 5. 解密
let jsonStr = SimApiAesUtil.decrypt(req._data, key.getOrThrow())
let jsonStr = SimApiAesUtil.decrypt(req.data, key.getOrThrow())
if (jsonStr.isEmpty()) {
SimApiError.error(code: 400, message: "解密失败")
}
+4 -4
View File
@@ -84,7 +84,7 @@ public class SimApiAuth {
public func login(loginItem: SimApiLoginItem, expireSeconds!: Int64 = 604800, token!: String = ""): String {
let newToken = if (token.isEmpty()) { generateToken() } else { token }
let tokenKey = "${tokenCachePrefix}${newToken}"
let setKey = "${tokenSetCachePrefix}${loginItem._id}"
let setKey = "${tokenSetCachePrefix}${loginItem.id}"
let json = loginItemJson(loginItem)
if (let Some(redis) <- _redis) {
@@ -93,10 +93,10 @@ public class SimApiAuth {
redis.expire(setKey, expireSeconds)
} else {
_tokenStore[newToken] = TokenEntry(json, nowMillis() + expireSeconds * 1000)
var tokens = _userTokens.get(loginItem._id)
var tokens = _userTokens.get(loginItem.id)
if (tokens == None) {
tokens = HashSet<String>()
_userTokens[loginItem._id] = tokens.getOrThrow()
_userTokens[loginItem.id] = tokens.getOrThrow()
}
tokens.getOrThrow().add(newToken)
}
@@ -187,7 +187,7 @@ public class SimApiAuth {
public func logout(token: String): Unit {
let item = getLogin(token)
if (let Some(item) <- item) {
removeTokenOfUser(item._id, token)
removeTokenOfUser(item.id, token)
}
let tokenKey = "${tokenCachePrefix}${token}"
if (let Some(redis) <- _redis) {
+2 -2
View File
@@ -131,8 +131,8 @@ public open class SimApiHttpClient {
SimApiError.errorWhenFalse(response.isSuccessStatusCode, code: response.statusCode, message: "HTTP ERROR: ${response.statusCode}")
let json = response.content.readAsString()
let result = JsonSerializer.Deserialize<SimApiResponse<T>>(json)
SimApiError.errorWhen(result._code != 200, code: result._code, message: result._message)
return result._data.getOrThrow()
SimApiError.errorWhen(result.code != 200, code: result.code, message: result.message)
return result.data.getOrThrow()
} finally {
response.close()
}
+1 -1
View File
@@ -268,7 +268,7 @@ struct SimApiActionInvoker {
let requiredTypes = auth.`type`.split(",")
var matched = false
for (t in requiredTypes) {
if (loginItem._types.contains(t)) {
if (loginItem.types.contains(t)) {
matched = true
break
}
+27 -3
View File
@@ -172,10 +172,34 @@ public class SimApiUtil {
}
/**
* 将对象序列化为 JSON 字符串(委托给 SimApiJson.json 统一实现,对齐 C# SimApiUtil.Json)。
* 将对象序列化为 JSON 字符串(对齐 C# SimApiUtil.Json)。
* @param obj 任意对象(None 输出 null)。
*/
public static func json(obj: ?Any): String {
SimApiJson.json(obj)
if (let Some(obj) <- obj) {
return JsonSerializer.Serialize(obj)
}
"null"
}
/**
* JSON 字符串转义(对齐 C# 内部转义逻辑)。
* @param s 原始字符串。
* @return 转义后可直接放入 JSON 字符串字面量的内容。
*/
public static func escapeJson(s: String): String {
var sb = StringBuilder()
for (c in s.runes()) {
match (c) {
case '"' => sb.append("\\\"")
case '\\' => sb.append("\\\\")
case '\n' => sb.append("\\n")
case '\r' => sb.append("\\r")
case '\t' => sb.append("\\t")
case _ => sb.append(c)
}
}
sb.toString()
}
/**
@@ -194,7 +218,7 @@ public class SimApiUtil {
* @return Base64 字符串。
*/
public static func base64Encode(obj: Any): String {
let json = SimApiJson.json(Some(obj))
let json = json(Some(obj))
base64Encode(json)
}
+1 -1
View File
@@ -61,7 +61,7 @@ public class SimApiExceptionMiddleware <: IMiddleware {
}
var response = SimApiBaseResponse(simEx.code, message)
if (context.response.statusCode == 404) {
response._message = "接口不存在"
response.message = "接口不存在"
}
return response
}
@@ -13,6 +13,7 @@ import soulsoft_extensions_logging.*
import simapi_serialization.*
import simapi.communications.*
import simapi.configurations.*
import simapi.helpers.*
/**
* 请求日志中间件:记录请求方法、URL、请求头、请求体、响应状态码、耗时与异常。
@@ -93,7 +94,7 @@ public class SimApiRequestLogMiddleware <: IMiddleware {
var first = true
for ((name, values) in context.request.headers) {
if (!first) { sb.append(",") }
sb.append("\"${SimApiJson.escapeJson(name)}\":\"${SimApiJson.escapeJson(joinValues(values))}\"")
sb.append("\"${SimApiUtil.escapeJson(name)}\":\"${SimApiUtil.escapeJson(joinValues(values))}\"")
first = false
}
sb.append("}\n")
@@ -147,15 +148,15 @@ public class SimApiRequestLogMiddleware <: IMiddleware {
var first = true
for ((k, v) in map) {
if (!first) { sb.append(",") }
sb.append("\"${SimApiJson.escapeJson(k)}\":")
sb.append("\"${SimApiUtil.escapeJson(k)}\":")
if (let s: String <- v) {
if (s.size > maxLen) {
sb.append("\"${SimApiJson.escapeJson(s[0..maxLen])}...(${s.size})\"")
sb.append("\"${SimApiUtil.escapeJson(s[0..maxLen])}...(${s.size})\"")
} else {
sb.append("\"${SimApiJson.escapeJson(s)}\"")
sb.append("\"${SimApiUtil.escapeJson(s)}\"")
}
} else {
sb.append(SimApiJson.json(Some(v)))
sb.append(SimApiUtil.json(Some(v)))
}
first = false
}