Files
simapi-net/README.md
T

677 lines
23 KiB
Markdown
Raw Normal View History

2026-05-20 10:53:41 +08:00
# SimApi for .NET
2019-12-23 16:50:49 +08:00
2026-05-20 10:53:41 +08:00
> NuGet: `Simcu.SimApi` | 目标框架: `net8.0` / `net9.0` / `net10.0`
> 作者: xRain@SimcuTeam
ASP.NET Core API 基础框架库,提供统一异常拦截、响应封装、Token 认证、Swagger 文档、S3 存储、MQTT 通信、Hangfire 任务调度、Auth Center 网关鉴权与 IAM 权限管理。
2019-12-23 16:50:49 +08:00
2026-04-07 10:44:34 +08:00
---
2019-12-23 16:50:49 +08:00
2026-05-20 10:53:41 +08:00
## 快速开始
2026-04-07 10:44:34 +08:00
```csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSimApi(options =>
2019-12-23 16:50:49 +08:00
{
2026-04-07 10:44:34 +08:00
options.RedisConfiguration = "localhost:6379";
options.EnableSimApiAuth = true;
options.EnableSimApiDoc = true;
options.ConfigureSimApiDoc(doc =>
2019-12-23 16:50:49 +08:00
{
2026-05-20 10:53:41 +08:00
doc.DocumentTitle = "我的API";
doc.ApiGroups = [new("api", "公共接口"), new("admin", "管理接口")];
2026-04-07 10:44:34 +08:00
});
});
var app = builder.Build();
2026-04-26 10:03:38 +08:00
app.UseSimApi();
2026-04-07 10:44:34 +08:00
app.Run();
```
---
2026-05-20 10:53:41 +08:00
## 核心概念
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
### 统一响应格式
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
所有接口输出 JSON,HTTP 状态码始终 `200`,错误信息在 `code` 字段:
2026-04-07 10:44:34 +08:00
2026-06-23 15:25:35 +08:00
| code | 含义 |
| ---- | ---------- |
| 200 | 成功 |
| 204 | 无数据 |
| 400 | 参数错误 |
| 401 | 需要登录 |
| 403 | 无权访问 |
| 404 | 资源不存在 |
| 500 | 服务器错误 |
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
### 异常处理流程
2026-04-26 10:03:38 +08:00
```
2026-05-20 10:53:41 +08:00
请求 → SimApiExceptionMiddleware(全异常捕获→HTTP 200+JSON)
→ SimApiAuthMiddleware(Token→LoginInfo)
→ [SimApiSign] Filter(签名)
→ [SimApiAuth] Filter(登录检查)
→ OnActionExecuting(模型验证→code 400)
→ Action
→ SimApiResponseFilter(封装响应)
2026-04-26 10:03:38 +08:00
```
---
2026-05-20 10:53:41 +08:00
## 项目结构
2026-04-26 10:03:38 +08:00
```
2026-05-20 10:53:41 +08:00
SimApi/
├── Attributes/ # [SimApiAuth] [SimApiDoc] [SimApiSign] [AesBody] [SynapseEvent] [SynapseRpc] [OriginResponse]
├── AuthSDK/ # Auth Center & IAM SDK
│ ├── SimApiAuthClient.cs HTTP 客户端(签名调用 Auth Center
│ ├── SimApiAuthCenter.cs Auth Center API 封装
│ ├── SimApiAuthCenterDto.cs 数据模型
│ ├── SimApiAuthCenterMiddleware.cs 网关鉴权中间件
│ ├── SimApiAuthIam.cs IAM 权限管理 API
│ └── SimApiAuthIamDto.cs IAM 数据模型
├── Communications/ # SimApiBaseResponse, PageResponse<T>, SimApiLoginItem, SimApiBaseRequest
├── Configurations/ # SimApiOptions + 各模块 Option 类
├── Controllers/ # SimApiBaseController, SimApiCommonController, SimApiAuthController
├── Helpers/ # SimApiError, SimApiAuth, SimApiCache, SimApiHttpClient, SimApiStorage, SimApiUtil, SimApiAesUtil
├── Interfaces/ # ISimApiAuthChecker
├── Middlewares/ # SimApiExceptionMiddleware, SimApiAuthMiddleware
├── Synapse/ # MQTT Pub/Sub + RPC + Config Store
├── Exceptions/ # SimApiException
├── Models/ # SimApiBaseModel
├── SwaggerFilters/ # 文档自动过滤器
├── ModelBinders/ # AesBody, SimApiSign
├── Logger/ # 彩色控制台日志
└── SimApiExtensions.cs # AddSimApi + UseSimApi
2026-04-26 10:03:38 +08:00
```
---
2026-05-20 10:53:41 +08:00
## 1. 错误处理 — SimApiError
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
独立静态类 `SimApi.Helpers.SimApiError`。所有错误最终 → `throw new SimApiException(code, message)` → 中间件捕获。
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
**使用**: 文件顶部加 `using static SimApi.Helpers.SimApiError;`
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
### 完整方法签名
2026-04-26 10:03:38 +08:00
```csharp
2026-05-20 10:53:41 +08:00
// 直接抛错
void Error(int code = 500, string message = "");
// condition 为 true 时抛错 (code 默认 400)
void ErrorWhen([DoesNotReturnIf(true)] bool condition, int code = 400, string message = "");
// 同上(别名)
void ErrorWhenTrue([DoesNotReturnIf(true)] bool condition, int code = 400, string message = "");
// condition 为 false 时抛错
void ErrorWhenFalse([DoesNotReturnIf(false)] bool condition, int code = 400, string message = "");
// obj 为 null 时抛错 (code 默认 404)
void ErrorWhenNull([NotNull] object? condition, int code = 404, string message = "");
2026-04-26 10:03:38 +08:00
```
2026-05-20 10:53:41 +08:00
### 示例
2026-04-26 10:03:38 +08:00
```csharp
using static SimApi.Helpers.SimApiError;
2026-05-20 10:53:41 +08:00
var user = db.Users.Find(id);
ErrorWhenNull(user, 404, "用户不存在");
ErrorWhen(amount <= 0, 400, "金额无效");
2026-04-26 10:03:38 +08:00
ErrorWhenFalse(hasPermission, 403, "无权操作");
2026-05-20 10:53:41 +08:00
Error(500, "服务器内部错误");
2026-04-07 10:44:34 +08:00
```
---
2026-05-20 10:53:41 +08:00
## 2. 控制器 — SimApiBaseController
2026-04-07 10:44:34 +08:00
2026-04-26 10:03:38 +08:00
```csharp
2026-05-20 10:53:41 +08:00
public class SimApiBaseController : Controller
2026-04-07 10:44:34 +08:00
{
2026-05-20 10:53:41 +08:00
// 当前登录信息(需 EnableSimApiAuth
2026-04-26 10:03:38 +08:00
protected SimApiLoginItem LoginInfo => (SimApiLoginItem)HttpContext.Items["LoginInfo"]!;
2026-05-20 10:53:41 +08:00
protected string LoginToken => (string)HttpContext.Items["LoginToken"]!;
// OnActionExecuting 自动验证 ModelState → 无效时抛 code 400
2026-04-07 10:44:34 +08:00
}
```
2026-05-20 10:53:41 +08:00
**内建**: `[Consumes("application/json")]` + `[Produces("application/json")]`
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
### 自动路由
2026-04-07 10:44:34 +08:00
2026-06-23 15:25:35 +08:00
| 路由 | 方法 | 条件 | 说明 |
| ----------------------- | -------- | -------------------------------------------- | ------------------------ |
| `/versions` | GET/POST | `VersionRoute != null`(默认) | 返回 SimApi/App 版本 |
| `/user/info` | POST | `EnableSimApiAuth` + `UserInfoRoute != null` | 需登录,返回 LoginInfo |
| `/auth/logout` | POST | `EnableSimApiAuth` + `LogoutRoute != null` | 退出登录(可自定义路由) |
| `/swagger` | GET | `EnableSimApiDoc` | Swagger UI |
| `/jobs` | GET | `EnableJob` + `DashboardUrl != null` | Hangfire 控制台 |
| `/exception/{code:int}` | GET | 始终 | 错误反馈页面 |
2026-05-20 10:53:41 +08:00
### 返回值规范
2026-06-23 15:25:35 +08:00
| 场景 | 返回类型 |
| ---------- | ------------------------- |
| 写操作 | `void` |
| 单条查询 | 直接 Entity |
| 列表查询 | `Entity[]` |
| 分页 | `PageResponse<Entity[]>` |
| 自定义状态 | `SimApiBaseResponse` |
| 跳封装 | 方法加 `[OriginResponse]` |
2026-05-20 10:53:41 +08:00
---
## 3. 认证系统 — SimApiAuth
2026-04-07 10:44:34 +08:00
```csharp
2026-05-20 10:53:41 +08:00
string Login(SimApiLoginItem loginItem, TimeSpan? expireTime = null, string? token = null); // 默认7天
string Update(SimApiLoginItem loginItem, string token);
SimApiLoginItem? GetLogin(string token);
SimApiLoginItem[] GetAllLogins(string userId);
void Logout(string token);
void LogoutAll(string userId);
```
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
```csharp
// LoginItem 结构
public class SimApiLoginItem {
required string Id;
string[] Type = ["user"];
Dictionary<string, string> Meta = [];
Dictionary<string, object?> Extra = [];
2026-04-26 10:03:38 +08:00
}
2026-04-07 10:44:34 +08:00
```
2026-05-20 10:53:41 +08:00
**Token 传参**: Header `Token: <value>`
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
### 认证后处理 Hook — ISimApiAuthChecker
2026-04-26 10:03:38 +08:00
```csharp
2026-05-20 10:53:41 +08:00
public interface ISimApiAuthChecker {
void Run(SimApiLoginItem loginItem, string token);
2026-04-26 10:03:38 +08:00
}
2026-05-20 10:53:41 +08:00
// 实现后自动注册为 Scoped,每次认证后调用
2026-04-26 10:03:38 +08:00
```
---
2026-05-20 10:53:41 +08:00
## 4. AuthSDK — Auth Center 网关 & IAM
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
启用: `EnableSimApiAuthGate = true`
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
### 4.1 配置
2026-04-26 10:03:38 +08:00
```csharp
2026-05-20 10:53:41 +08:00
options.EnableSimApiAuthGate = true;
options.ConfigureSimApiAuthCenter(gate =>
2026-04-26 10:03:38 +08:00
{
2026-05-20 10:53:41 +08:00
gate.Server = "https://auth.coce.cc";
gate.AppId = "your-app-id";
gate.AppKey = "your-app-key";
gate.UseMiddleware = true; // 内部应用启用,解析 X-SimApi-Gate-Auth Header
gate.UseIam = true; // 启用 IAM
});
2026-04-26 10:03:38 +08:00
```
2026-05-20 10:53:41 +08:00
### 4.2 SimApiAuthClient
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
继承 `SimApiHttpClient`,自动配置 Server/AppId/AppKey,对 Auth Center 发起签名请求。
### 4.3 SimApiAuthCenter — Auth Center API
```csharp
// === 签名验证 ===
void VerifySign(string appId, string timestamp, string nonce, string sign);
// === 群组 ===
GroupRelatedItem[]? GroupRelated(string profileId);
AppAndProfileItem[]? GroupSearch(string keyword, int skip = 0, int take = 20);
GroupDetailTreeNode? GroupDetail(string groupId, string profileId);
string[]? GroupRelatedIndex(string groupId, string profileId);
// === Profile ===
AppAndProfileItem[]? ProfileSearch(string keyword, int skip = 0, int take = 20);
AppAndProfileItem[]? ProfileList(string[] ids);
// === 内部应用专用 ===
bool CheckIsAppOwner(string profileId, string applicationId);
AppAndProfileItem[]? GetAppList(string profileId, IEnumerable<string> appIds);
// === 登录 ===
GetCodeResponse GetLoginCode(string? scene = null, Dictionary<string, object>? data = null, string? backUrl = null);
LoginInfoResponse GetLoginInfo(string code, string? scene = null);
// === 安全验证(二次确认) ===
GetCodeResponse GetConfirmCode(string scene, string userId, Dictionary<string, object>? data = null, string? backUrl = null);
ConfirmResponse Confirm(string code, string scene, string? userId = null);
```
**GetCodeResponse**: `{ Code, Server, FullUrl }`
### 4.4 SimApiAuthIam — IAM 权限管理
```csharp
void RegisterPermissions(PermissionItem[] permissions);
string[] GetPermissionOwned(string profileId);
void CheckPermission(string profileId, string permission);
```
**PermissionItem**: `{ Identifier, Name, Group, Description }`
### 4.5 SimApiAuthCenterMiddleware — 网关鉴权
内部应用专用。解析请求头 `X-SimApi-Gate-Auth` / `X-SimApi-Gate-Time` / `X-SimApi-Gate-Sign`MD5 验签后将 Base64 解码的用户信息注入 `HttpContext.Items["LoginInfo"]`
2026-04-07 10:44:34 +08:00
2026-04-26 10:03:38 +08:00
---
2026-05-20 10:53:41 +08:00
## 5. Attributes 完整参考
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
### [SimApiAuth] — 身份认证
2026-04-07 10:44:34 +08:00
```csharp
2026-05-20 10:53:41 +08:00
[SimApiAuth] // 仅检查登录
[SimApiAuth("admin")] // 单角色
[SimApiAuth("admin,manager")] // 逗号分隔 OR 关系
[SimApiAuth(new[]{"a","b"})] // 数组形式
2026-04-26 10:03:38 +08:00
```
2026-05-20 10:53:41 +08:00
- 加在 **Controller 类****方法**
- **禁止** 代码中手动判断 `LoginInfo.Type.Contains(...)` 做权限控制
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
### [SimApiDoc] — Swagger 文档注解
2026-04-26 10:03:38 +08:00
```csharp
[SimApiDoc("分组名", "接口名称")]
[SimApiDoc("分组名", "接口名称", "详细描述")]
[SimApiDoc(new[]{"tag1","tag2"}, "接口名称")]
```
2026-05-20 10:53:41 +08:00
### [SimApiSign] — API 签名验证
2026-04-07 10:44:34 +08:00
```csharp
2026-04-26 10:03:38 +08:00
[SimApiSign(KeyProvider = typeof(MySignProvider))]
2026-05-20 10:53:41 +08:00
// 签名: MD5(field1=v1&...&appId=xxx&timestamp=ts&nonce=nnn&密钥)
```
实现 `SimApiSignProviderBase`
```csharp
public class MySignProvider : SimApiSignProviderBase
{
// 可覆盖: AppIdName, TimestampName, NonceName, SignName, QueryExpires(秒), DuplicateRequestProtection, SignFields
public override string? GetKey(string? appId) { ... }
}
```
### [AesBody] — AES 解密请求体
```csharp
[AesBody(KeyProvider = typeof(MyAesKeyProvider))] MyRequest req
// 客户端提交: {"data": "Base64(AES-256-CBC 密文)"}
```
实现 `AesBodyProviderBase`,覆盖 `AppIdName``GetKey(appId)`
### [SynapseEvent] — MQTT 事件处理
```csharp
[SynapseEvent("order/created")] // 指定 eventName
[SynapseEvent] // 不指定 = 方法名
2026-06-23 15:25:35 +08:00
// 参数: 1个(string eventName) / 2个(string eventName, T data)
// 注意: 至少需要1个参数
2026-05-20 10:53:41 +08:00
```
### [SynapseRpc] — MQTT RPC 方法
```csharp
[SynapseRpc] // 注册名 "ClassName.MethodName"
[SynapseRpc("customName")] // 自定义名
// 参数: 0~2个,第2个固定 Dictionary<string,string>(headers)
```
### [OriginResponse] — 跳过响应封装
```csharp
[HttpGet][OriginResponse]
public string GetRaw() => "raw";
2026-04-07 10:44:34 +08:00
```
---
2026-05-20 10:53:41 +08:00
## 6. Swagger 文档 — EnableSimApiDoc
2026-04-07 10:44:34 +08:00
```csharp
options.ConfigureSimApiDoc(doc =>
{
doc.DocumentTitle = "接口文档";
2026-05-20 10:53:41 +08:00
doc.ApiGroups = [new("api", "公共"), new("admin", "管理", "描述可选")];
doc.SupportedMethod = [SubmitMethod.Post]; // 默认仅POST
doc.ApiAuth = new SimApiAuthOption { Type = ["SimApiAuth"] }; // 认证方式
2026-04-07 10:44:34 +08:00
});
```
2026-05-20 10:53:41 +08:00
每组通过 `[ApiExplorerSettings(GroupName = "admin")]` 分类。
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
### 自动过滤器
2026-04-07 10:44:34 +08:00
2026-06-23 15:25:35 +08:00
| 过滤器 | 效果 |
| --------------------------------- | ------------------------------------ |
| `SimApiResponseOperationFilter` | 返回值包装为 `SimApiBaseResponse<T>` |
| `SimApiAuthOperationFilter` | 鉴权接口 + Token Header |
| `SimApiSignOperationFilter` | 签名接口注入签名参数 |
| `AesBodyOperationFilter` | AES 接口展示原始结构 |
| `GlobalDynamicObjectSchemaFilter` | object/Dictionary → Schema |
| `RemoveEmptyTagsFilter` | 清除空分组 |
2026-04-07 10:44:34 +08:00
---
2026-05-20 10:53:41 +08:00
## 7. 对象存储 — EnableSimApiStorage
2026-04-07 10:44:34 +08:00
2026-04-26 10:03:38 +08:00
基于 MinIO SDKS3 兼容)。
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
### 配置
2026-04-07 10:44:34 +08:00
```csharp
options.EnableSimApiStorage = true;
options.ConfigureSimApiStorage(s =>
{
2026-05-20 10:53:41 +08:00
s.Endpoint = "http://minio:9000"; // 不能以 / 结尾
2026-04-07 10:44:34 +08:00
s.AccessKey = "admin";
2026-04-26 10:03:38 +08:00
s.SecretKey = "pass";
2026-05-20 10:53:41 +08:00
s.Bucket = "bucket";
s.ServeUrl = "http://cdn.example.com/bucket"; // 不能以 / 结尾
2026-04-07 10:44:34 +08:00
});
```
2026-05-20 10:53:41 +08:00
### API
2026-04-07 10:44:34 +08:00
```csharp
2026-05-20 10:53:41 +08:00
GetUploadUrlResponse GetUploadUrl(string path, int expire = 7200);
// 返回: { UploadUrl, DownloadUrl, Path }
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
string GetDownloadUrl(string path, int expire = 600);
void UploadFile(string path, Stream stream, string contentType = "image/png");
string? FullUrl(string? path); // 路径→完整URL
string? GetUrl(string? path); // 同上
string? GetPath(string? url); // URL→相对路径
IMinioClient Client { get; } // 底层 MinIO 客户端
```
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
> **路径必须以 `/` 开头**
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
---
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
## 8. Redis 缓存 — SimApiCache
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
依赖 `RedisConfiguration`Key 自动加前缀 `SimApi:Cache:`
```csharp
void Set(string key, object value, DistributedCacheEntryOptions? options = null);
T? Get<T>(string key);
string? Get(string key);
bool HasKey(string key);
void Remove(string key);
2026-04-07 10:44:34 +08:00
```
---
2026-05-20 10:53:41 +08:00
## 9. HTTP 客户端 — SimApiHttpClient
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
用于调用其他带签名/AES 的 SimApi 服务。
### 属性
2026-04-07 10:44:34 +08:00
2026-04-26 10:03:38 +08:00
```csharp
2026-05-20 10:53:41 +08:00
virtual string Server { get; init; }
virtual string AppId { get; init; }
virtual string AppKey { get; init; }
virtual string SignName { get; init; } = "sign";
virtual string TimestampName { get; init; } = "timestamp";
virtual string NonceName { get; init; } = "nonce";
virtual string? AppIdName { get; init; } = "appId";
virtual string[] SignFields { get; init; } = [];
```
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
### 调用方法
```csharp
2026-06-23 15:25:35 +08:00
T SignQuery<T>(string url, object? body = null, Dictionary<string, string>? queries = null);
T AesQuery<T>(string url, object body);
T AesSignQuery<T>(string url, object body, Dictionary<string, string>? queries = null);
2026-04-26 10:03:38 +08:00
```
---
2026-05-20 10:53:41 +08:00
## 10. 任务调度 — EnableJob
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
Hangfire + Redis。
2026-04-07 10:44:34 +08:00
```csharp
options.EnableJob = true;
options.ConfigureSimApiJob(job =>
{
2026-05-20 10:53:41 +08:00
job.DashboardUrl = "/jobs"; // null = 不开启
2026-04-07 10:44:34 +08:00
job.DashboardAuthUser = "admin";
2026-05-20 10:53:41 +08:00
job.DashboardAuthPass = "pass";
job.Database = 1; // Redis DB 编号
2026-04-26 10:03:38 +08:00
job.Servers = [
2026-05-20 10:53:41 +08:00
new() { Queues = ["default"], WorkerNum = 5 },
new() { Queues = ["email"], WorkerNum = 2 }
2026-04-07 10:44:34 +08:00
];
});
```
```csharp
2026-05-20 10:53:41 +08:00
BackgroundJob.Enqueue(() => DoWork());
BackgroundJob.Schedule(() => DoWork(), TimeSpan.FromMinutes(5));
RecurringJob.AddOrUpdate("id", () => DoWork(), Cron.Daily);
BackgroundJob.ContinueJobWith(id, () => Step2());
2026-04-07 10:44:34 +08:00
```
---
2026-05-20 10:53:41 +08:00
## 11. MQTT 通信 — EnableSynapse
2026-04-07 10:44:34 +08:00
2026-04-26 10:03:38 +08:00
基于 MQTTnet v5WebSocket 连接。
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
### 配置
2026-04-07 10:44:34 +08:00
```csharp
options.EnableSynapse = true;
options.ConfigureSimApiSynapse(s =>
{
2026-04-26 10:03:38 +08:00
s.Websocket = "ws://mqtt:8083/mqtt";
s.Username = "user";
s.Password = "pass";
2026-05-20 10:53:41 +08:00
s.SysName = "my-system";
s.AppName = "order-service";
s.AppId = "instance-001"; // 不填自动GUID
s.RpcTimeout = 3; // 秒
s.EventLoadBalancing = false; // $queue 负载均衡
s.EnableConfigStore = true; // 分布式配置中心
2026-04-07 10:44:34 +08:00
});
```
2026-05-20 10:53:41 +08:00
### Topic 规则
2026-04-07 10:44:34 +08:00
2026-06-23 15:25:35 +08:00
| 用途 | Topic 格式 |
| -------- | ------------------------------------------------------ |
| 事件发布 | `{SysName}/event/{AppName}/{eventName}` |
| 事件订阅 | `{SysName}/event/{eventName}` (或 `$queue/` 前缀) |
| RPC 请求 | `{SysName}/{targetApp}/rpc/server/{method}` |
2026-04-07 10:44:34 +08:00
| RPC 响应 | `{SysName}/{callerApp}/rpc/client/{AppId}/{messageId}` |
2026-06-23 15:25:35 +08:00
| 配置 | `{SysName}/synapse-config-store/{key}` (Retain) |
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
### API
2026-04-07 10:44:34 +08:00
```csharp
2026-05-20 10:53:41 +08:00
// 事件
bool Event(string eventName, dynamic? param = null);
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
// RPC (同步)
SimApiBaseResponse<T> Rpc<T>(string appName, string method, dynamic? param = null,
Dictionary<string, string>? headers = null, int? timeout = null);
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
// RPC 内报错
void RpcError(int code, string message = "");
void RpcErrorWhen(bool condition, int code, string message = "");
2026-04-07 10:44:34 +08:00
2026-04-26 10:03:38 +08:00
// 分布式配置
2026-05-20 10:53:41 +08:00
bool SetConfig(string key, string value);
string? GetConfig(string key);
2026-04-07 10:44:34 +08:00
```
2026-05-20 10:53:41 +08:00
### 处理器扫描(自动注册)
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
`[SynapseRpc]` / `[SynapseEvent]` 的类自动扫描注册为 Scoped 服务。
2019-12-23 16:50:49 +08:00
2026-04-07 10:44:34 +08:00
---
2019-12-23 16:50:49 +08:00
2026-05-20 10:53:41 +08:00
## 12. AES 加解密 — SimApiAesUtil
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
AES-256-CBC + PKCS7,密钥经 SHA256 处理。
2026-04-07 10:44:34 +08:00
```csharp
2026-04-26 10:03:38 +08:00
string cipher = SimApiAesUtil.Encrypt("明文", "任意长度密钥");
2026-04-07 10:44:34 +08:00
string plain = SimApiAesUtil.Decrypt(cipher, "任意长度密钥");
```
---
2026-05-20 10:53:41 +08:00
## 13. 工具集 — SimApiUtil
2026-04-26 10:03:38 +08:00
2026-04-07 10:44:34 +08:00
```csharp
2026-05-20 10:53:41 +08:00
DateTime CstNow; // UTC+8
double TimestampNow; // 秒级 Unix
string SimApiVersion; // NuGet 包版本
string AppVersion; // 宿主应用版本
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
string Md5(string src, string mode = "x2"); // x2=32位, x3=48位, x4=64位
string Sha1(string src, string mode = "x2");
string Base64Encode(string str);
string Base64Decode(string base64Str);
string Base64Encode(object obj);
T? Base64Decode<T>(string base64Str);
2026-04-07 10:44:34 +08:00
2026-05-20 10:53:41 +08:00
string Json(object? obj); // camelCase,中文不转义
T? FromJson<T>(string json);
T XmlDeserialize<T>(string xml);
JsonSerializerOptions JsonOption; // 可复用
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
bool CheckCell(string cell); // 手机号验证
bool CheckEmail(string email);
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
IQueryable<T> Paginate<T>(this IQueryable<T> query, int page, int count);
2026-04-07 10:44:34 +08:00
```
---
2026-05-20 10:53:41 +08:00
## 14. 数据模型 — SimApiBaseModel
2026-04-26 10:03:38 +08:00
```csharp
public class UserEntity : SimApiBaseModel
{
2026-05-20 10:53:41 +08:00
// 自动: Id(GUID string), CreatedAt, UpdatedAt
// 默认忽略映射: Id, CreatedAt, UpdatedAt
2026-04-26 10:03:38 +08:00
}
2026-05-20 10:53:41 +08:00
void MapData<TS>(TS source, bool mapAll = false); // 同名同类型非null属性
void MapData<TS>(TS source, string[] mapFields); // 指定字段
void UpdateTime();
2026-04-26 10:03:38 +08:00
```
---
2026-05-20 10:53:41 +08:00
## 15. DTO 规范
2026-04-26 10:03:38 +08:00
2026-06-23 15:25:35 +08:00
| 类型 | 命名 | 示例 |
| ---- | ----------------- | ----------------- |
| 请求 | `[动作]Request` | `UserEditRequest` |
| 响应 | `[动作]Response` | `TokenResponse` |
| 载体 | `[含义]Data/Item` | `GenerateData` |
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
### 框架内置 DTO
2026-04-26 10:03:38 +08:00
```csharp
2026-05-20 10:53:41 +08:00
class SimApiStringIdOnlyRequest { required string Id; }
class SimApiOneFieldRequest<T> { T? Data; }
class SimApiBasePageRequest { int Page = 1; int Count = 20; }
class SimApiBaseResponse { int Code; string Message; } // code=200 默认
class SimApiBaseResponse<T> : SimApiBaseResponse { T? Data; }
class PageResponse<T> { T? List; int Page; int Count; int Total; }
2026-04-26 10:03:38 +08:00
```
---
2026-05-20 10:53:41 +08:00
## 16. SimApiOptions 完整配置
2026-04-26 10:03:38 +08:00
```csharp
2026-04-07 10:44:34 +08:00
builder.Services.AddSimApi(options =>
{
2026-05-20 10:53:41 +08:00
options.RedisConfiguration = "localhost:6379";
2026-04-26 10:03:38 +08:00
2026-05-20 10:53:41 +08:00
// 功能开关
options.EnableSimApiAuth = false; // Token 认证
options.EnableSimApiAuthGate = false; // Auth Center 网关鉴权
options.EnableSimApiDoc = false; // Swagger 文档
options.EnableSimApiStorage = false; // S3 存储
options.EnableJob = false; // Hangfire
options.EnableSynapse = false; // MQTT
options.EnableSimApiHttpClient = false; // 外部 HTTP 调用
options.EnableLogger = true; // 控制台日志
options.EnableCors = true; // 全量 CORS
options.EnableSimApiException = true; // 全局异常拦截
options.EnableSimApiResponseFilter = true; // 响应统一封装
options.EnableForwardHeaders = true; // 反向代理 Header
options.EnableLowerUrl = true; // URL 小写
// 子模块配置
options.ConfigureSimApiDoc(doc => { ... });
options.ConfigureSimApiStorage(s => { ... });
options.ConfigureSimApiJob(job => { ... });
options.ConfigureSimApiSynapse(s => { ... });
options.ConfigureSimApiAuthCenter(gate => { ... });
options.ConfigureSimApiHttpClient(http => { ... });
options.ConfigureSimApiRoute(route => { ... });
options.ConfigureSimApiException(ex => { ... });
2026-04-07 10:44:34 +08:00
});
```
---
2026-05-20 10:53:41 +08:00
## 17. GOTCHAS — 常见错误
2026-04-07 10:44:34 +08:00
2026-06-23 15:25:35 +08:00
| ❌ 错误 | ✅ 正确 |
| ------------------------------------------------------ | ------------------------------------------- |
| 存储路径 `avatars/file.jpg`(无前导 `/`) | 必须以 **`/`** 开头 |
| `s.Endpoint = "http://x:9000/"` | **不能以 `/` 结尾** |
| `synapse.PublishEvent(...)` | 方法名是 **`synapse.Event(...)`** |
| `synapse.CallRpcAsync(...)` | 方法名是 **`synapse.Rpc<T>(...)`** |
| HTTP 4xx/5xx 状态码 | 永远 **HTTP 200**,错误在 JSON code |
| `SupportedMethod` 写多种方法 | 默认仅 **POST** |
2026-05-20 10:53:41 +08:00
| `SimApiStorageOptions = Configuration.GetSection(...)` | 用 **`ConfigureSimApiStorage(s => {...})`** |
2026-06-23 15:25:35 +08:00
| 代码中 `LoginInfo.Type.Contains("admin")` | 用 `[SimApiAuth("admin")]` |
| `return ActionResult<T>` | 直接返回 Entity / void |
2026-04-07 10:44:34 +08:00
---
2026-05-20 10:53:41 +08:00
## 18. 禁止事项
2026-04-07 10:44:34 +08:00
2026-06-23 15:25:35 +08:00
| ❌ 禁止 | ✅ 正确 |
| --------------------------------------- | ----------------------------------------------------- |
| HTTP 4xx/5xx 表达业务错误 | HTTP 200 + JSON `code` |
| `throw new Exception(msg)` | `ErrorWhen``throw new SimApiException(code, msg)` |
| 鉴权 Attribute 只放方法 | 可以放 Controller **类**上 |
| 手动判断 `LoginInfo.Type.Contains(...)` | `[SimApiAuth("role")]` |
| Entity 配导航属性 / Fluent API | Convention 自动映射 |
| 花括号块命名空间 | 文件范围 `namespace X;` |
| 传统构造函数注入 | 主构造函数 |
| `new List<T>()` / `new string[]{}` | `[]` 集合表达式 |
| `Count() > 0` | `Any()` |
| `ToList()` → 数组 | 直接 `ToArray()` |
| 全局 catch 吞异常 | 让异常冒泡到 SimApiExceptionMiddleware |