Files
simapi-cj/README.md
T
2026-08-16 12:46:15 +08:00

327 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SimApi for Cangjiesimapi
> 仓颉版 SimApiASP.NET Core 风格 API 基础框架,移植自 C# 项目 [SimApi](https://github.com/SimcuTeam/simapi-net)`E:\simcu\simapi-net`)。
提供**统一响应格式、异常拦截、Token 认证、缓存、工具集、HTTP 客户端**等 API 基础能力。
---
## 快速开始
```cangjie
package your_app
import soulsoft_web_http.*
import soulsoft_web_routing.*
import soulsoft_web_hosting.*
import soulsoft_extensions_logging.*
import soulsoft_extensions_injection.*
import simapi.extensions.*
import simapi.communications.*
main(args: Array<String>) {
let builder = WebHost.createBuilder(args)
builder.services.addRouting()
builder.services.addLogging()
// 注册 SimApi 服务(与 addLogging 同样式)
builder.addSimApi { options =>
options.enableSimApiAuth = true // Token 认证(未配 Redis 自动用 InMemory
options.enableSimApiCache = true // 缓存
options.enableSimApiException = true // 全局异常拦截
}
let host = builder.build()
host.useSimApi()
// 业务接口:返回统一响应格式
host.mapGet("hello") {
context =>
context.response.write(SimApiBaseResponse().toJsonString(dataJson: "\"hello cangjie.\""))
}
host.run()
}
```
### 统一响应格式
所有接口输出 JSON,HTTP 状态码始终 `200`,错误信息在 `code` 字段:
| code | 含义 |
| ---- | ---------- |
| 200 | 成功 |
| 204 | 无数据 |
| 400 | 参数错误 |
| 401 | 需要登录 |
| 403 | 无权访问 |
| 404 | 资源不存在 |
| 500 | 服务器错误 |
### 异常处理流程
```
请求 → SimApiExceptionMiddleware(全异常捕获→HTTP 200+JSON)
→ SimApiAuthMiddleware(Token→LoginInfo)
→ 路由 → 业务处理
```
---
## 项目结构
```
simapi-cj/
├── cjpm.toml # 包配置
├── src/
│ ├── communications/ # SimApiBaseResponse, PageResponse, SimApiLoginItem, ApiResult, 请求 DTO
│ ├── configurations/ # SimApiOptions + 各模块 Option(含 ConfigureSimApiXxx 回调)
│ ├── controllers/ # SimApiBaseController, SimApiCommonController, SimApiAuthControllerMVC 写法)
│ ├── exceptions/ # SimApiException
│ ├── extensions/ # SimApiExtensionsaddSimApi / useSimApi + 内置路由)
│ ├── helpers/ # SimApiError, SimApiUtil, SimApiAuth, SimApiCache, SimApiHttpClient
│ ├── interfaces/ # ISimApiAuthChecker
│ ├── logger/ # SimApiLogger, SimApiLoggerProvider(彩色日志)
│ └── middlewares/ # SimApiExceptionMiddleware, SimApiAuthMiddleware, SimApiRequestLogMiddleware
```
---
## 模块说明
### 1. 错误处理 — SimApiError
```cangjie
import simapi.helpers.*
SimApiError.error(500, "服务器内部错误") // 直接抛错
SimApiError.errorWhen(amount <= 0, 400, "金额无效") // 条件为 true 时抛错
SimApiError.errorWhenFalse(hasPermission, 403, "无权操作")
SimApiError.errorWhenNone(someOptional, 404, "用户不存在")
```
### 2. 认证 — SimApiAuth
```cangjie
import simapi.helpers.*
import simapi.communications.*
let auth = SimApiAuth(redisConfiguration: "") // 配 Redis 用 Redis,否则 InMemory
let token = auth.login(SimApiLoginItem(id: "user-001")) // 默认 7 天
let login = auth.getLogin(token) // 获取登录信息
auth.logout(token) // 退出登录
auth.logoutAll("user-001") // 退出全部
```
- **Redis 模式**:配置 `RedisConfiguration`(如 `"localhost:6379"`)时使用,支持多实例共享
- **InMemory 模式**:零配置,适合开发/测试;重启后登录态丢失
- **Token 传参**Header `Token: <value>` 或 Query `token=<value>`
### 3. 缓存 — SimApiCache
```cangjie
let cache = SimApiCache(redisConfiguration: "")
cache.set("key", "value")
let v = cache.get("key") // ?String
cache.hasKey("key") // Bool
cache.remove("key")
```
Key 自动加前缀 `SimApi:Cache:`
### 4. 工具集 — SimApiUtil
```cangjie
SimApiUtil.cstNow // UTC+8 时间
SimApiUtil.timestampNow // 秒级时间戳
SimApiUtil.md5("text") // 32 位十六进制
SimApiUtil.sha1("text") // 40 位
SimApiUtil.base64Encode("text") / base64Decode("...")
SimApiUtil.checkCell("13800138000") // 手机号
SimApiUtil.checkEmail("a@b.com") // 邮箱
```
### 5. HTTP 客户端 — SimApiHttpClient
用于调用其他带签名/AES 的 SimApi 服务:
```cangjie
let client = SimApiHttpClient(options: SimApiHttpClientOptions()) // 配置 server/appId/appKey
// 返回泛型 T(对齐 .NET SignQuery<T>/AesQuery<T>/AesSignQuery<T>),T 需实现 ISerialization<T>
let resp1 = client.signQuery<SimApiLoginItem>("/api/hello", body: "{\"a\":1}")
let resp2 = client.aesQuery<SimApiLoginItem>("/api/data", body: "{\"a\":1}")
let resp3 = client.aesSignQuery<SimApiLoginItem>("/api/data", body: "{\"a\":1}")
```
### 5.1 请求日志 — enableRequestLog
记录每次请求的方法、URL、请求头、请求体、响应状态码与耗时:
```cangjie
builder.addSimApi { options =>
options.enableRequestLog = true
options.simApiRequestLogOptions.showFullHeader = true // 打印完整 Header(默认只打 Token/Query-Id
options.simApiRequestLogOptions.requestStringLogLength = 200 // 请求体截断长度(0 不截断)
}
```
输出示例:
```
[GET] /hello
*( RequestHeaders [Full] ) =>
{"host":"127.0.0.1:5000",...}
*( RequestBody ) =>
*( Response [200] ) => 1.756400ms
```
### 5.2 日志格式 — SimApiLogger
`enableLogger`(默认 `true`)时自动使用 `SimApiLoggerProvider`(替换 soulsoft 默认控制台格式),输出格式对齐 C# 原版:
```
[ 分类 ][ 时间:毫秒 ][ 级别 ]
消息内容
```
按级别着色:
| 级别 | 颜色 |
|------|------|
| Debug | 深紫(DarkMagenta |
| Info | 深青(DarkCyan |
| Warn | 黄(Yellow |
| Error | 红(Red |
| Fatal | 深红(DarkRed 粗体近似) |
| 其他 | 白(White |
### 6. 内置路由(UseSimApi 自动注册)
| 路由 | 方法 | 条件 | 说明 |
| ----------------- | -------- | ---------------------------- | -------------------------- |
| `/versions` | GET/POST | 始终 | 返回 SimApi/App 版本 |
| `/user/info` | POST | `enableSimApiAuth` | 需登录,返回 LoginInfo |
| `/auth/logout` | POST | `enableSimApiAuth` | 退出登录 |
| `/exception/{code}` | GET | 始终 | 错误反馈 |
### 7. 认证后处理 Hook — ISimApiAuthChecker
```cangjie
import simapi.interfaces.*
class MyAuthChecker <: ISimApiAuthChecker {
public func run(loginItem: SimApiLoginItem, token: String): Unit {
// 认证成功后执行
}
}
```
---
## SimApiOptions 完整配置
```cangjie
builder.addSimApi { options =>
options.redisConfiguration = "localhost:6379" // Redis(可选)
// 功能开关
options.enableSimApiAuth = false // Token 认证
options.enableSimApiCache = true // 缓存
options.enableSimApiException = true // 全局异常拦截
options.enableSimApiResponseFilter = true // 响应统一封装
options.enableSimApiHttpClient = false // HTTP 客户端
options.enableRequestLog = false // 请求日志中间件
options.enableCors = true // 全量 CORS
options.enableLogger = true // 控制台日志
// .NET 风格子模块配置回调(对齐 C# ConfigureSimApiXxx
options.configureSimApiRoute { route =>
route.userInfoRoute = Some("user/info")
route.logoutRoute = Some("auth/logout")
}
options.configureSimApiRequestLog { opt =>
opt.showFullResponse = true
opt.showFullHeader = false
opt.requestStringLogLength = 50
}
options.configureSimApiHttpClient { http =>
http.appId = "your-app-id"
http.appKey = "your-app-key"
http.server = "https://api.example.com"
}
}
```
## 内置控制器(MVC 写法)
simapi 提供 Spire MVC 控制器(继承 `SimApiBaseController`),宿主通过 `addControllers` + 手动 `AssemblyPart` 注册(当前 cjc 无法自动扫描包子包):
| 控制器 | 路由 | 说明 |
|--------|------|------|
| `SimApiCommonController` | `/exception/{code}``/webconfig``/user/info` | 通用内置路由 |
| `SimApiAuthController` | `/auth/logout` | 退出登录 |
| `SimApiBaseController` | — | 基类:`loginInfo` / `loginToken` / `requireLogin()` |
```cangjie
import simapi.controllers.*
// 控制器写法:继承 SimApiBaseController,注解路由 + DI 注入
public class MyController <: SimApiBaseController {
private let _auth: SimApiAuth
public init(auth: SimApiAuth) { this._auth = auth }
@HttpPost["my/route"]
public func myAction(@FromBody request: MyRequest): ApiResult {
requireLogin()
ApiResult.ok()
}
}
// 宿主注册
let mvc = builder.services.addControllers()
mvc.addApplicationPart(AssemblyPart("simapi.controllers", [
TypeInfo.of<SimApiCommonController>(),
TypeInfo.of<SimApiAuthController>(),
]))
```
---
## 未实现模块(选项占位)
以下 C# 原包功能因仓颉生态暂无对应库,**选项保留但未实现**:
| 选项 | 原功能 | 状态 |
|------|--------|------|
| `enableSimApiDoc` | Swagger 文档 | ❌ 未实现 |
| `enableSimApiStorage` | S3/MinIO 存储 | ❌ 未实现 |
| `enableSynapse` | MQTT 通信 | ❌ 未实现 |
| `enableJob` | Hangfire 任务调度 | ❌ 未实现 |
| `enableSimApiAuthGate` | Auth Center 网关鉴权 | ❌ 未实现 |
| `SimApiAesUtil` | AES-256-CBC | ⚠️ 仓颉 std 无 AES,暂用 Base64 占位 |
---
## 依赖
| 依赖 | 用途 |
|------|------|
| `soulsoft_web_http / routing / hosting` | Web 框架(ASP.NET Core 仓颉移植版) |
| `soulsoft_extensions_logging` 系列 | 日志 |
| `soulsoft_extensions_injection` | 依赖注入 |
| `soulsoft_extensions_configuration` | 配置 |
| `soulsoft_serialization` | JSON 序列化 |
| `redis`pkg.cangjie-lang.cn | Redis 客户端(认证/缓存 Redis 模式) |
| `stdx`CANGJIE_STDX_PATH | 标准扩展库(md5/sha1/base64/http |
> 构建前需设置 `CANGJIE_STDX_PATH` 指向本地 stdx 的 `static/stdx` 目录。
---
## 许可证
MIT