Skip to content

Repository files navigation

SimApi for .NET

NuGet: Simcu.SimApi | 目标框架: net8.0 / net9.0 / net10.0
作者: xRain@SimcuTeam

ASP.NET Core API 基础框架库,提供统一异常拦截、响应封装、Token 认证、Swagger 文档、S3 存储、MQTT 通信、Hangfire 任务调度、Auth Center 网关鉴权与 IAM 权限管理。


快速开始

varbuilder=WebApplication.CreateBuilder(args);builder.Services.AddSimApi(options =>{// RedisConfiguration 可选:配置则使用 Redis,不配则自动使用 InMemoryoptions.RedisConfiguration="localhost:6379";options.EnableSimApiAuth=true;options.EnableSimApiDoc=true;options.ConfigureSimApiDoc(doc =>{doc.DocumentTitle="我的API";doc.ApiGroups=[new("api","公共接口"),new("admin","管理接口")];});});varapp=builder.Build();app.UseSimApi();app.Run();

核心概念

统一响应格式

所有接口输出 JSON,HTTP 状态码始终 200,错误信息在 code 字段:

code含义
200成功
204无数据
400参数错误
401需要登录
403无权访问
404资源不存在
500服务器错误

异常处理流程

请求 → SimApiExceptionMiddleware(全异常捕获→HTTP 200+JSON)
→ SimApiAuthMiddleware(Token→LoginInfo)
→ [SimApiSign] Filter(签名)
→ [SimApiAuth] Filter(登录检查)
→ OnActionExecuting(模型验证→code 400)
→ Action
→ SimApiResponseFilter(封装响应)

项目结构

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

1. 错误处理 — SimApiError

独立静态类 SimApi.Helpers.SimApiError。所有错误最终 → throw new SimApiException(code, message) → 中间件捕获。

使用: 文件顶部加 using static SimApi.Helpers.SimApiError;

完整方法签名

// 直接抛错voidError(intcode=500,stringmessage="");// condition 为 true 时抛错 (code 默认 400)voidErrorWhen([DoesNotReturnIf(true)]boolcondition,intcode=400,stringmessage="");// 同上(别名)voidErrorWhenTrue([DoesNotReturnIf(true)]boolcondition,intcode=400,stringmessage="");// condition 为 false 时抛错voidErrorWhenFalse([DoesNotReturnIf(false)]boolcondition,intcode=400,stringmessage="");// obj 为 null 时抛错 (code 默认 404)voidErrorWhenNull([NotNull]object?condition,intcode=404,stringmessage="");

示例

usingstaticSimApi.Helpers.SimApiError;varuser=db.Users.Find(id);ErrorWhenNull(user,404,"用户不存在");ErrorWhen(amount<=0,400,"金额无效");ErrorWhenFalse(hasPermission,403,"无权操作");Error(500,"服务器内部错误");

2. 控制器 — SimApiBaseController

publicclassSimApiBaseController:Controller{// 当前登录信息(需 EnableSimApiAuth)protectedSimApiLoginItemLoginInfo=>(SimApiLoginItem)HttpContext.Items["LoginInfo"]!;protectedstringLoginToken=>(string)HttpContext.Items["LoginToken"]!;// OnActionExecuting 自动验证 ModelState → 无效时抛 code 400}

内建: [Consumes("application/json")] + [Produces("application/json")]

自动路由

路由方法条件说明
/versionsGET/POSTVersionRoute != null(默认)返回 SimApi/App 版本
/user/infoPOSTEnableSimApiAuth + UserInfoRoute != null需登录,返回 LoginInfo
/auth/logoutPOSTEnableSimApiAuth + LogoutRoute != null退出登录(可自定义路由)
/swaggerGETEnableSimApiDocSwagger UI
/jobsGETEnableJob + DashboardUrl != nullHangfire 控制台
/exception/{code:int}GET始终错误反馈页面

返回值规范

场景返回类型
写操作void
单条查询直接 Entity
列表查询Entity[]
分页PageResponse<Entity[]>
自定义状态SimApiBaseResponse
跳封装方法加 [OriginResponse]

3. 认证系统 — SimApiAuth

stringLogin(SimApiLoginItemloginItem,TimeSpan?expireTime=null,string?token=null);// 默认7天stringUpdate(SimApiLoginItemloginItem,stringtoken);SimApiLoginItem?GetLogin(stringtoken);SimApiLoginItem[]GetAllLogins(stringuserId);voidLogout(stringtoken);voidLogoutAll(stringuserId);
// LoginItem 结构publicclassSimApiLoginItem{requiredstringId;string[]Type=["user"];Dictionary<string,string>Meta=[];Dictionary<string,object?>Extra=[];}

Token 传参: Header Token: <value>

存储模式

SimApiAuth 内部自动判断,无需手动配置:

条件存储后端用户↔Token 映射
配置了 RedisConfigurationRedis(IDistributedCache + Set)Redis Set
未配置 RedisConfigurationInMemory(DistributedMemoryCacheConcurrentDictionary
  • Redis 模式:支持多实例共享,Token 持久化,适合生产环境
  • InMemory 模式:零配置即可启用 EnableSimApiAuth,适合开发/测试/单实例场景。注意重启后所有登录态丢失
// 不配 Redis 也能用 Authbuilder.Services.AddSimApi(options =>{options.EnableSimApiAuth=true;// 自动使用 InMemory});

认证后处理 Hook — ISimApiAuthChecker

publicinterfaceISimApiAuthChecker{voidRun(SimApiLoginItemloginItem,stringtoken);}// 实现后自动注册为 Scoped,每次认证后调用

4. AuthSDK — Auth Center 网关 & IAM

启用: EnableSimApiAuthGate = true

4.1 配置

options.EnableSimApiAuthGate=true;options.ConfigureSimApiAuthCenter(gate =>{gate.Server="https://auth.coce.cc";gate.AppId="your-app-id";gate.AppKey="your-app-key";gate.UseMiddleware=true;// 内部应用启用,解析 X-SimApi-Gate-Auth Headergate.UseIam=true;// 启用 IAM});

4.2 SimApiAuthClient

继承 SimApiHttpClient,自动配置 Server/AppId/AppKey,对 Auth Center 发起签名请求。

4.3 SimApiAuthCenter — Auth Center API

// === 签名验证 ===voidVerifySign(stringappId,stringtimestamp,stringnonce,stringsign);// === 群组 ===GroupRelatedItem[]?GroupRelated(stringprofileId);AppAndProfileItem[]?GroupSearch(stringkeyword,intskip=0,inttake=20);GroupDetailTreeNode?GroupDetail(stringgroupId,stringprofileId);string[]?GroupRelatedIndex(stringgroupId,stringprofileId);// === Profile ===AppAndProfileItem[]?ProfileSearch(stringkeyword,intskip=0,inttake=20);AppAndProfileItem[]?ProfileList(string[]ids);// === 内部应用专用 ===boolCheckIsAppOwner(stringprofileId,stringapplicationId);AppAndProfileItem[]?GetAppList(stringprofileId,IEnumerable<string>appIds);// === 登录 ===GetCodeResponseGetLoginCode(string?scene=null,Dictionary<string,object>?data=null,string?backUrl=null);LoginInfoResponseGetLoginInfo(stringcode,string?scene=null);// === 安全验证(二次确认) ===GetCodeResponseGetConfirmCode(stringscene,stringuserId,Dictionary<string,object>?data=null,string?backUrl=null);ConfirmResponseConfirm(stringcode,stringscene,string?userId=null);

GetCodeResponse: { Code, Server, FullUrl }

4.4 SimApiAuthIam — IAM 权限管理

voidRegisterPermissions(PermissionItem[]permissions);string[]GetPermissionOwned(stringprofileId);voidCheckPermission(stringprofileId,stringpermission);

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"]


5. Attributes 完整参考

[SimApiAuth] — 身份认证

[SimApiAuth]// 仅检查登录[SimApiAuth("admin")]// 单角色[SimApiAuth("admin,manager")]// 逗号分隔 OR 关系[SimApiAuth(new[]{"a","b"})]// 数组形式
  • 加在 Controller 类方法
  • 禁止 代码中手动判断 LoginInfo.Type.Contains(...) 做权限控制

[SimApiDoc] — Swagger 文档注解

[SimApiDoc("分组名","接口名称")][SimApiDoc("分组名","接口名称","详细描述")][SimApiDoc(new[]{"tag1","tag2"},"接口名称")]

[SimApiSign] — API 签名验证

[SimApiSign(KeyProvider=typeof(MySignProvider))]// 签名: MD5(field1=v1&...&appId=xxx&timestamp=ts&nonce=nnn&密钥)

实现 SimApiSignProviderBase

publicclassMySignProvider:SimApiSignProviderBase{// 可覆盖: AppIdName, TimestampName, NonceName, SignName, QueryExpires(秒), DuplicateRequestProtection, SignFieldspublicoverridestring?GetKey(string?appId){ ...}}

[AesBody] — AES 解密请求体

[AesBody(KeyProvider=typeof(MyAesKeyProvider))]MyRequest req
// 客户端提交: {"data": "Base64(AES-256-CBC 密文)"}

实现 AesBodyProviderBase,覆盖 AppIdNameGetKey(appId)

[SynapseEvent] — MQTT 事件处理

[SynapseEvent("order/created")]// 指定 eventName[SynapseEvent]// 不指定 = 方法名// 参数: 1个(string eventName) / 2个(string eventName, T data)// 注意: 至少需要1个参数

[SynapseRpc] — MQTT RPC 方法

[SynapseRpc]// 注册名 "ClassName.MethodName"[SynapseRpc("customName")]// 自定义名// 参数: 0~2个,第2个固定 Dictionary<string,string>(headers)

[OriginResponse] — 跳过响应封装

[HttpGet][OriginResponse]publicstringGetRaw()=>"raw";

6. Swagger 文档 — EnableSimApiDoc

options.ConfigureSimApiDoc(doc =>{doc.DocumentTitle="接口文档";doc.ApiGroups=[new("api","公共"),new("admin","管理","描述可选")];doc.SupportedMethod=[SubmitMethod.Post];// 默认仅POSTdoc.ApiAuth=newSimApiAuthOption{Type=["SimApiAuth"]};// 认证方式});

每组通过 [ApiExplorerSettings(GroupName = "admin")] 分类。

自动过滤器

过滤器效果
SimApiResponseOperationFilter返回值包装为 SimApiBaseResponse<T>
SimApiAuthOperationFilter鉴权接口 + Token Header
SimApiSignOperationFilter签名接口注入签名参数
AesBodyOperationFilterAES 接口展示原始结构
GlobalDynamicObjectSchemaFilterobject/Dictionary → Schema
RemoveEmptyTagsFilter清除空分组

7. 对象存储 — EnableSimApiStorage

基于 MinIO SDK(S3 兼容)。

配置

options.EnableSimApiStorage=true;options.ConfigureSimApiStorage(s =>{s.Endpoint="http://minio:9000";// 不能以 / 结尾s.AccessKey="admin";s.SecretKey="pass";s.Bucket="bucket";s.ServeUrl="http://cdn.example.com/bucket";// 不能以 / 结尾});

API

GetUploadUrlResponseGetUploadUrl(stringpath,intexpire=7200);// 返回: { UploadUrl, DownloadUrl, Path }stringGetDownloadUrl(stringpath,intexpire=600);voidUploadFile(stringpath,Streamstream,stringcontentType="image/png");string?FullUrl(string?path);// 路径→完整URLstring?GetUrl(string?path);// 同上string?GetPath(string?url);// URL→相对路径IMinioClientClient{get;}// 底层 MinIO 客户端

路径必须以 / 开头


8. 缓存 — SimApiCache

通过 EnableSimApiCache(默认 true)控制。Key 自动加前缀 SimApi:Cache:

存储后端与 SimApiAuth 一致:配了 RedisConfiguration 就用 Redis,否则用 InMemory。

voidSet(stringkey,objectvalue,DistributedCacheEntryOptions?options=null);T?Get<T>(stringkey);string?Get(stringkey);boolHasKey(stringkey);voidRemove(stringkey);

9. HTTP 客户端 — SimApiHttpClient

用于调用其他带签名/AES 的 SimApi 服务。

属性

virtualstringServer{get;init;}virtualstringAppId{get;init;}virtualstringAppKey{get;init;}virtualstringSignName{get;init;}="sign";virtualstringTimestampName{get;init;}="timestamp";virtualstringNonceName{get;init;}="nonce";virtualstring?AppIdName{get;init;}="appId";virtualstring[]SignFields{get;init;}=[];

调用方法

TSignQuery<T>(stringurl,object?body=null,Dictionary<string,string>?queries=null);TAesQuery<T>(stringurl,objectbody);TAesSignQuery<T>(stringurl,objectbody,Dictionary<string,string>?queries=null);

10. 任务调度 — EnableJob

Hangfire + Redis。

options.EnableJob=true;options.ConfigureSimApiJob(job =>{job.DashboardUrl="/jobs";// null = 不开启job.DashboardAuthUser="admin";job.DashboardAuthPass="pass";job.Database=1;// Redis DB 编号job.Servers=[new(){Queues=["default"],WorkerNum=5},new(){Queues=["email"],WorkerNum=2}];});
BackgroundJob.Enqueue(()=>DoWork());BackgroundJob.Schedule(()=>DoWork(),TimeSpan.FromMinutes(5));RecurringJob.AddOrUpdate("id",()=>DoWork(),Cron.Daily);BackgroundJob.ContinueJobWith(id,()=>Step2());

11. MQTT 通信 — EnableSynapse

基于 MQTTnet v5,WebSocket 连接。

配置

options.EnableSynapse=true;options.ConfigureSimApiSynapse(s =>{s.Websocket="ws://mqtt:8083/mqtt";s.Username="user";s.Password="pass";s.SysName="my-system";s.AppName="order-service";s.AppId="instance-001";// 不填自动GUIDs.RpcTimeout=3;// 秒s.EventLoadBalancing=false;// $queue 负载均衡s.EnableConfigStore=true;// 分布式配置中心});

Topic 规则

用途Topic 格式
事件发布{SysName}/event/{AppName}/{eventName}
事件订阅{SysName}/event/{eventName} (或 $queue/ 前缀)
RPC 请求{SysName}/{targetApp}/rpc/server/{method}
RPC 响应{SysName}/{callerApp}/rpc/client/{AppId}/{messageId}
配置{SysName}/synapse-config-store/{key} (Retain)

API

// 事件boolEvent(stringeventName,dynamic?param=null);// RPC (同步)SimApiBaseResponse<T>Rpc<T>(stringappName,stringmethod,dynamic?param=null,Dictionary<string,string>?headers=null,int?timeout=null);// RPC 内报错voidRpcError(intcode,stringmessage="");voidRpcErrorWhen(boolcondition,intcode,stringmessage="");// 分布式配置boolSetConfig(stringkey,stringvalue);string?GetConfig(stringkey);

处理器扫描(自动注册)

[SynapseRpc] / [SynapseEvent] 的类自动扫描注册为 Scoped 服务。


12. AES 加解密 — SimApiAesUtil

AES-256-CBC + PKCS7,密钥经 SHA256 处理。

stringcipher=SimApiAesUtil.Encrypt("明文","任意长度密钥");stringplain=SimApiAesUtil.Decrypt(cipher,"任意长度密钥");

13. 工具集 — SimApiUtil

DateTimeCstNow;// UTC+8doubleTimestampNow;// 秒级 UnixstringSimApiVersion;// NuGet 包版本stringAppVersion;// 宿主应用版本stringMd5(stringsrc,stringmode="x2");// x2=32位, x3=48位, x4=64位stringSha1(stringsrc,stringmode="x2");stringBase64Encode(stringstr);stringBase64Decode(stringbase64Str);stringBase64Encode(objectobj);T?Base64Decode<T>(stringbase64Str);stringJson(object?obj);// camelCase,中文不转义T?FromJson<T>(stringjson);TXmlDeserialize<T>(stringxml);JsonSerializerOptionsJsonOption;// 可复用boolCheckCell(stringcell);// 手机号验证boolCheckEmail(stringemail);IQueryable<T>Paginate<T>(thisIQueryable<T>query,intpage,intcount);

14. 数据模型 — SimApiBaseModel

publicclassUserEntity:SimApiBaseModel{// 自动: Id(GUID string), CreatedAt, UpdatedAt// 默认忽略映射: Id, CreatedAt, UpdatedAt}voidMapData<TS>(TSsource,boolmapAll=false);// 同名同类型非null属性voidMapData<TS>(TSsource,string[]mapFields);// 指定字段voidUpdateTime();

15. DTO 规范

类型命名示例
请求[动作]RequestUserEditRequest
响应[动作]ResponseTokenResponse
载体[含义]Data/ItemGenerateData

框架内置 DTO

classSimApiStringIdOnlyRequest{requiredstringId;}classSimApiOneFieldRequest<T>{T?Data;}classSimApiBasePageRequest{intPage=1;intCount=20;}classSimApiBaseResponse{intCode;stringMessage;}// code=200 默认classSimApiBaseResponse<T>:SimApiBaseResponse{T?Data;}classPageResponse<T>{T?List;intPage;intCount;intTotal;}

16. SimApiOptions 完整配置

builder.Services.AddSimApi(options =>{options.RedisConfiguration="localhost:6379";// 功能开关options.EnableSimApiAuth=false;// Token 认证options.EnableSimApiCache=true;// 缓存(Redis 或 InMemory)options.EnableSimApiAuthGate=false;// Auth Center 网关鉴权options.EnableSimApiDoc=false;// Swagger 文档options.EnableSimApiStorage=false;// S3 存储options.EnableJob=false;// Hangfireoptions.EnableSynapse=false;// MQTToptions.EnableSimApiHttpClient=false;// 外部 HTTP 调用options.EnableLogger=true;// 控制台日志options.EnableCors=true;// 全量 CORSoptions.EnableSimApiException=true;// 全局异常拦截options.EnableSimApiResponseFilter=true;// 响应统一封装options.EnableForwardHeaders=true;// 反向代理 Headeroptions.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 =>{ ...});});

17. GOTCHAS — 常见错误

❌ 错误✅ 正确
存储路径 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
SimApiStorageOptions = Configuration.GetSection(...)ConfigureSimApiStorage(s => {...})
代码中 LoginInfo.Type.Contains("admin")[SimApiAuth("admin")]
return ActionResult<T>直接返回 Entity / void

18. 禁止事项

❌ 禁止✅ 正确
HTTP 4xx/5xx 表达业务错误HTTP 200 + JSON code
throw new Exception(msg)ErrorWhenthrow new SimApiException(code, msg)
鉴权 Attribute 只放方法可以放 Controller
手动判断 LoginInfo.Type.Contains(...)[SimApiAuth("role")]
Entity 配导航属性 / Fluent APIConvention 自动映射
花括号块命名空间文件范围 namespace X;
传统构造函数注入主构造函数
new List<T>() / new string[]{}[] 集合表达式
Count() > 0Any()
ToList() → 数组直接 ToArray()
全局 catch 吞异常让异常冒泡到 SimApiExceptionMiddleware

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages