# BeautyNetWebApi **Repository Path**: leafCore/beauty-net-web-api ## Basic Information - **Project Name**: BeautyNetWebApi - **Description**: 基于 .NET 8 的微服务全家桶框架,对主流中间件与工业通信协议进行二次封装,提供统一接口、高可用保障与开箱即用的开发体验。覆盖消息队列、缓存、搜索、网关、认证、工业物联网通信、实时推送等场景,适合企业级后端基础设施搭建和工业 IoT 平台开发。 - **Primary Language**: C# - **License**: MIT - **Default Branch**: master - **Homepage**: https://gitee.com/leafCore/beauty-net-web-api - **GVP Project**: No ## Statistics - **Stars**: 18 - **Forks**: 1 - **Created**: 2025-11-04 - **Last Updated**: 2026-06-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Beauty.Net 基于 .NET 8 的微服务全家桶框架,对主流中间件进行二次封装,提供统一接口、高可用保障与开箱即用的体验。 ## 架构范式 每个微服务自带 Ocelot 网关(自网关模式),通过 Consul 实现服务发现,JWT 在网关层统一鉴权,验证通过后通过 Ocelot 转发请求到下游控制器。 ``` ┌─ Consul ───────────────────────┐ │ 服务注册 / 健康检查 / 服务发现 │ └──────────┬──────────────────────┘ │ ┌───────────────────────────┼───────────────────────────┐ │ │ │ ┌──────┴──────────────┐ ┌───────┴──────────────┐ ┌───────┴──────────────┐ │ Beauty.NetWebApi │ │ Beauty.Net.Demo │ │ Beauty.Net.XXX │ │ (主网关 + 自身服务) │ │ (订单服务 + 自网关) │ │ (更多微服务...) │ │ 端口: 5083 │ │ 端口: 5084 │ │ 端口: 5085 │ │ │ │ │ │ │ │ ┌─ Ocelot网关 ────┐│ │ ┌─ Ocelot网关 ─────┐│ │ ┌─ Ocelot网关 ─────┐│ │ │ JWT鉴权 ││ │ │ JWT鉴权 ││ │ │ JWT鉴权 ││ │ │ 路由转发 ││ │ │ 路由转发 ││ │ │ 路由转发 ││ │ │ /api/* → self││ │ │ /api/order/*→self ││ │ │ /api/xxx/* →self ││ │ │ /api/order/*→ Demo││ │ │ ││ │ │ ││ │ └──────────────────┘│ │ └──────────────────┘│ │ └──────────────────┘│ │ │ │ │ │ │ │ ┌─ 自身服务 ────────┐│ │ ┌─ 自身服务 ───────┐│ │ ┌─ 自身服务 ───────┐│ │ │ WeatherForecast ││ │ │ OrderController ││ │ │ XxxController ││ │ │ CacheDemo ││ │ │ HealthController ││ │ │ ... ││ │ │ AuthController ││ │ │ ││ │ │ ││ │ └──────────────────┘│ │ └──────────────────┘│ │ └──────────────────┘│ └─────────────────────┘ └─────────────────────┘ └─────────────────────┘ ``` ## 核心价值 - **模块化** — 每个中间件独立为 NuGet 风格的类库,按需引用 - **高可用内置** — 各模块自带重试、熔断、降级、健康检查等生产级保障 - **统一接口** — 一致的命名、一致的 DI 注册方式、一致的配置模型 - **开箱即用** — 提供 Demo 控制器、appsettings 示例和 Docker Compose 模板 - **IoT 全栈** — Modbus TCP/RTU + Siemens S7 PLC + MQTT + SignalR 实时通信 ## 架构概览 ``` 请求管道 ExceptionHandler → CORS → Swagger → UseRouting → UseAuthorization → 日志中间件 → 响应包装中间件 → HealthChecks → RabbitMQ消费者 → 微服务网关 → MapControllers 模块分层 Beauty.Net.Integration ← 集成层:Consul + Ocelot 协同 Beauty.Net.Mqtt ← IoT 通信层:MQTT 发布/订阅(高可用) Beauty.Net.SignalR ← 实时通信层:Chat + Notification 双 Hub Beauty.Net.S7 ← IoT 通信层:Siemens S7 PLC 通信 Beauty.Net.Modbus ← IoT 通信层:Modbus TCP/RTU 通用对接 Beauty.Net.Middleware ← 横切层:日志、响应包装 Beauty.Net.Ocelot ← 网关层:路由、限流、熔断 Beauty.Net.Consul ← 服务发现:注册、健康检查、负载均衡 Beauty.Net.Cache ← 缓存层:Memory / Redis(FreeRedis) Beauty.Net.Kafka ← 消息队列层:Kafka 生产/消费 Beauty.Net.RabbitMQ ← 消息队列层:6 种 RabbitMQ 模式 Beauty.Net.Elasticsearch ← 搜索层:Elasticsearch 索引/查询 Beauty.Net.MongoDB ← 数据层:MongoDB CRUD Beauty.Net.JsonWebToken ← 认证层:JWT 签发/验证/撤销 Beauty.Net.MiniExcel ← 工具层:Excel 导入导出 ``` ## 项目结构 ``` Beauty.Net/ ├── Beauty.Net.Cache/ # 缓存模块(内存 / Redis + 熔断 + 防击穿) ├── Beauty.Net.Consul/ # 服务发现(缓存降级 + 多策略负载均衡) ├── Beauty.Net.Elasticsearch/ # 搜索引擎(Sniffing 节点发现 + 批量 API) ├── Beauty.Net.Integration/ # 集成层(Consul + Ocelot 协同启动) ├── Beauty.Net.JsonWebToken/ # JWT 认证(黑名单撤销 + 精确错误信息) ├── Beauty.Net.Kafka/ # Kafka 消息队列(死信队列 + Lag 监控) ├── Beauty.Net.Middleware/ # 中间件(全链路 TraceId + 慢请求预警) ├── Beauty.Net.MiniExcel/ # Excel 工具(流式 + 文件大小保护) ├── Beauty.Net.Modbus/ # Modbus TCP/RTU(零依赖 + 类型化读写 + 4 种字节序) ├── Beauty.Net.MongoDB/ # MongoDB 数据访问(事务 + 重试 + 分页) ├── Beauty.Net.Mqtt/ # MQTT 客户端(自动重连 + 消息缓存 + 通配符订阅) ├── Beauty.Net.Ocelot/ # API 网关(熔断 + QoS + 配置热重载) ├── Beauty.Net.RabbitMQ/ # RabbitMQ 消息队列(6 模式 + 发布确认 + 通道池) ├── Beauty.Net.S7/ # Siemens S7 PLC(全系列 + 9 种数据类型 + 便捷方法) ├── Beauty.Net.SignalR/ # SignalR 实时通信(Chat + Notification 双 Hub + HTML 客户端) └── Beauty.NetWebApi/ # 主入口项目 ``` ## 路由鉴权字段说明 | 字段 | 说明 | |------|------| | `AuthenticationProviderKey` | 设为 `"Bearer"` 时 Ocelot 会触发 JwtBearer 验证;设为 `null` 或省略则公开访问 | | `AddClaimsToRequest` | JWT Claims 到下游 Header 的映射,`{"sub":"X-User-Id"}` 表示把 `sub` Claim 写入 Header `X-User-Id` | | `ServiceName` | Consul 中的服务名,Ocelot 通过它自动发现下游实例 | | `LoadBalancerOptions` | 负载均衡策略:`RoundRobin`、`LeastConnection` 等 | ## 模块详解 ### Beauty.Net.Cache — 缓存模块 | 特性 | 说明 | |------|------| | 统一接口 | `ICacheService`,Memory / Redis 自由切换 | | 熔断保护 | 连续失败 N 次自动熔断,30s 后半开尝试恢复 | | 防缓存击穿 | `GetOrSet` 使用 Redis `SETNX` 分布式锁,仅一个线程回源 | | Pipeline 批量 | ≥10 个 key 时自动走 Redis Pipeline | | 启动验证 | `IHostedService` 启动时 Ping Redis 验证连接 | ```csharp // Program.cs builder.Services.AddMemoryCacheService(); // 内存缓存 builder.Services.AddRedisCacheService("127.0.0.1:6379"); // Redis 缓存 // 使用 app.MapGet("/product/{id}", async (int id, ICacheService cache) => { return await cache.GetOrSetAsync( $"product:{id}", async ct => await db.GetProductAsync(id, ct), TimeSpan.FromMinutes(30)); }); ``` ### Beauty.Net.Consul — 服务发现 | 特性 | 说明 | |------|------| | 重试机制 | 注册/查询失败自动重试,指数退避 | | 缓存降级 | 本地缓存服务实例列表,Consul 不可用时回退 | | 负载均衡 | 加权随机 / 轮询 / 最少连接数 三种策略 | | 心跳自愈 | 心跳失败自动重新注册服务 | ### Beauty.Net.JsonWebToken — JWT 认证 | 特性 | 说明 | |------|------| | 访问令牌 + 刷新令牌 | `CreateAccessToken` / `CreateRefreshToken` | | 令牌撤销 | 内存黑名单 + `Timer` 定期清理过期条目 | | 精确错误信息 | 按异常类型返回 "Token has expired." 等 | | 密钥长度验证 | < 32 字符开发环境警告并补齐,生产环境抛异常 | ### Beauty.Net.Ocelot — API 网关 | 特性 | 说明 | |------|------| | 网关鉴权 | `AuthenticationProviderKey` 绑定 JwtBearer,Ocelot 层拦截未授权请求 | | Claims 透传 | `AddClaimsToRequest` 将 JWT Claims 映射为 Header 注入下游请求 | | 熔断器 | 连续 N 次异常 → 熔断 30s → 半开探测 | | QoS 并发控制 | MaxConcurrentRequests + Timeout 防止过载 | | 配置热重载 | `FileSystemWatcher` 监听 `ocelot.json` 变更自动热加载 | | 配置回退 | 解析失败时回退到上一个有效配置 | ### Beauty.Net.Integration — 集成层 | 特性 | 说明 | |------|------| | 一行启动 | `ConfigureMicroservices()` 同时初始化 Consul + Ocelot | | 降级模式 | Consul 不可用时自动降级,服务以单机模式运行 | | 路由注册 | `ConfigureGatewayRoutes()` 代码层注册路由,`ocelot.json` 配置层热重载 | ### Beauty.Net.Kafka — Kafka 消息队列 | 特性 | 说明 | |------|------| | 生产者重试 | 发送失败指数退避重试 | | 死信队列 | 消费重试耗尽后自动发送到 `{topic}.dlq` | | Lag 监控 | `GetPartitionLag()` 实时查询消费进度 | | 优雅关闭 | 收到取消信号后等待当前消息处理完成 | ### Beauty.Net.RabbitMQ — RabbitMQ 消息队列 | 特性 | 说明 | |------|------| | 6 种消息模式 | Simple / Work / Pub-Sub / Routing / Topic / Headers | | 自动重连 | `ConnectionShutdown` 事件触发后台重连 | | 发布确认 | `WaitForConfirmsOrDie` + 重试 | | 死信交换机 | 消息 TTL 超时或消费失败后路由到 `dlx.exchange` | ### Beauty.Net.Elasticsearch — 搜索引擎 | 特性 | 说明 | |------|------| | Sniffing 节点发现 | 动态感知集群拓扑变化 | | 批量 API | `BulkIndexAsync` 一次性批量提交 | | 别名管理 | `SwapAliasAsync` 零停机索引切换 | ### Beauty.Net.MongoDB — 数据访问 | 特性 | 说明 | |------|------| | 重试机制 | `MongoConnectionException` / `TimeoutException` 自动退避重试 | | 事务支持 | `ExecuteInTransactionAsync` 封装 Session 生命周期 | | 分页查询 | `FindPagedAsync` 一次请求完成 Count + 分页 | ### Beauty.Net.Middleware — 中间件 | 特性 | 说明 | |------|------| | 全链路 TraceId | 每个请求生成 UUID TraceId,通过 `HttpContext.Items` 传播 | | 慢请求预警 | 耗时 ≥ 3s 自动 WARNING 级别日志 | | 请求/响应大小保护 | 请求体 > 1MB 截断,响应体 > 1MB 截断 | | 统一响应包装 | `ApiResponse` + `PaginatedResponse` 统一返回格式 | ### Beauty.Net.MiniExcel — Excel 工具 | 特性 | 说明 | |------|------| | 文件大小保护 | 默认 50MB 上限 | | 流式操作 | `ExportToStreamAsync` / `ImportFromStreamAsync` | | CancellationToken | 全部异步方法支持取消 | ### Beauty.Net.Modbus — Modbus TCP/RTU 通用对接 | 特性 | 说明 | |------|------| | 双模式 | TCP (Socket) / RTU (SerialPort) 自由切换 | | 八大功能码 | FC01~06, FC0F, FC10 全覆盖 | | 零外部依赖 | TCP/RTU 帧构建、CRC16 校验全部自实现 | | 类型化读写 | Bool / Int16 / UInt16 / Int32 / UInt32 / Float32 / Float64 + 4 种字节序 | | 批量读取 | `ReadMultipleAddressesAsync` 多不连续地址批量读取 | ```csharp // Program.cs builder.Services.AddModbus(options => configuration.GetSection("Modbus").Bind(options)); // 使用 var result = await modbus.ReadHoldingRegistersAsync(startAddress: 0, quantity: 4); var temp = await modbus.ReadTypedAsync(100, ModbusDataType.Float32, ModbusByteOrder.BigEndian); await modbus.WriteTypedAsync(100, 26.5f, ModbusDataType.Float32); ``` ### Beauty.Net.S7 — Siemens S7 PLC 通信 | 特性 | 说明 | |------|------| | 全系列支持 | S7-200 / 300 / 400 / 1200 / 1500 | | 6 种存储区 | I / Q / M / DB / T / C | | 9 种数据类型 | Bit / Byte / Int / Word / DInt / DWord / Real / String / ByteArray | | 便捷方法 | `ReadFloatAsync()` / `WriteInt32Async()` / `ReadStringAsync()` | | Rack/Slot 自动适配 | 通过配置灵活切换不同 CPU | ```csharp // Program.cs builder.Services.AddS7(options => configuration.GetSection("S7").Bind(options)); // 使用 var temp = await s7.ReadFloatAsync(S7Area.DataBlock, dbNumber: 1, startByte: 4); await s7.WriteInt32Async(S7Area.DataBlock, dbNumber: 1, startByte: 0, value: 9999); var flag = await s7.ReadBoolAsync(S7Area.Marker, dbNumber: 0, startByte: 10, bitOffset: 0); ``` ### Beauty.Net.Mqtt — 高可用 MQTT 发布/订阅 | 特性 | 说明 | |------|------| | 自动重连 | 断开后指数退避自动重建连接(1s → 2s → 4s → … → 30s) | | 消息缓存 | 断连期间自动缓存(上限 1000 条),重连后批量发送 | | 通配符订阅 | 内置 `+` / `#` 主题匹配引擎 | | 事件 + 处理器 | `MessageReceived` 全局事件 + `SubscribeAsync(topic, handler)` 专用处理器 | | 遗嘱消息 | 支持 LWT 遗嘱消息配置 | ```csharp // Program.cs builder.Services.AddMqtt(options => configuration.GetSection("MQTT").Bind(options)); // 使用 _mqtt.MessageReceived += (_, e) => Console.WriteLine($"[{e.Message.Topic}] {e.Message.GetPayloadString()}"); await _mqtt.SubscribeAsync("sensor/+/temperature", handler: async msg => { var data = msg.GetPayloadAs(); Console.WriteLine($"温度: {data?.Value}°C"); }); await _mqtt.PublishJsonAsync("sensor/temperature", new { value = 26.5, unit = "°C" }); ``` ### Beauty.Net.SignalR — 实时通信 | 特性 | 说明 | |------|------| | 双 Hub | ChatHub(私聊/群聊/广播) + NotificationHub(通知推送/主题订阅) | | ISignalRService | 从 Controller / BackgroundService 推送消息到客户端 | | 在线管理 | 自动维护在线用户列表,上下线事件广播 | | HTML 客户端 | 开箱即用的聊天室 Demo 页,支持私聊/群聊/通知 | | 自动重连 | 客户端自动重连(0s, 2s, 5s, 10s, 30s 阶梯退避) | ```csharp // Program.cs builder.Services.AddSignalRModule(options => configuration.GetSection("SignalR").Bind(options)); // ... app.MapControllers(); app.MapSignalRHubs(); // 从 Controller 推送 await _signalR.BroadcastMessageAsync(new ChatMessage { Message = "Hello!" }); await _signalR.BroadcastNotificationAsync(new NotificationMessage { Title = "告警", Level = NotificationLevel.Warning }); // Demo 页: http://localhost:5083/signalr-demo.html ``` ## 快速开始 ### 环境要求 - .NET 8.0 SDK - Visual Studio 2022 / Rider / VS Code ### 启动 ```bash git clone https://gitee.com/leafCore/beauty-net-web-api.git cd Beauty.NetWebApi dotnet restore dotnet run --project Beauty.NetWebApi ``` 启动后访问: ``` Swagger UI → http://localhost:5000/swagger 健康检查 → http://localhost:5000/health ``` ## 配置文件(appsettings.json) 所有模块配置均可通过 `appsettings.json` 管理: ```json { "JwtOptions": { "SecretKey": "your-secret-key-at-least-32-characters", "Issuer": "Beauty.Net", "Audience": "Beauty.Net", "AccessTokenExpirationMinutes": 30, "RefreshTokenExpirationHours": 24 }, "RabbitMQ": { "HostName": "localhost", "Port": 5672 }, "Kafka": { "BootstrapServers": "localhost:9092", "GroupId": "beauty-net-consumer" }, "Elasticsearch": { "Nodes": ["http://localhost:9200"] }, "MongoDB": { "ConnectionString": "mongodb://localhost:27017" }, "Consul": { "Enabled": true, "Address": "http://127.0.0.1:8500", "ServiceName": "Beauty.NetWebApi", "ServicePort": 5083, "ServiceAddress": "http://127.0.0.1", "EnableRegistration": true }, "Ocelot": { "ConfigurationPath": "ocelot.json", "EnableConsulServiceDiscovery": true, "ReLoadOnChange": true }, "Modbus": { "ConnectionType": "TCP", "SlaveId": 1, "Tcp": { "Host": "192.168.0.100", "Port": 502 } }, "S7": { "Host": "192.168.0.10", "CpuType": "S71200", "Rack": 0, "Slot": 0 }, "MQTT": { "Host": "localhost", "Port": 1883, "AutoConnect": true, "MaxReconnectAttempts": 10 }, "SignalR": { "ServerTimeoutSeconds": 30, "KeepAliveSeconds": 15, "EnableDetailedErrors": true } } ``` ## Docker Compose(依赖服务) ```yaml services: # ═══ 基础中间件 ═══ redis: image: redis:latest ports: ["6379:6379"] rabbitmq: image: rabbitmq:management ports: ["5672:5672", "15672:15672"] kafka: image: confluentinc/cp-kafka:latest ports: ["9092:9092"] environment: KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 elasticsearch: image: elasticsearch:8.12.0 ports: ["9200:9200"] environment: discovery.type: single-node xpack.security.enabled: false consul: image: consul:latest ports: ["8500:8500"] mongodb: image: mongo:latest ports: ["27017:27017"] # ═══ IoT 通信 ═══ mosquitto: image: eclipse-mosquitto:latest ports: ["1883:1883", "9001:9001"] volumes: - ./mosquitto.conf:/mosquitto/config/mosquitto.conf # ═══ 开发/测试用模拟器(可选) ═══ modbus-emulator: image: oqtlabs/modbus-simulator:latest # Modbus TCP 仿真器,端口 502 ports: ["502:502"] profiles: ["dev"] s7-emulator: image: dreibh/s7-server:latest # S7 PLC 仿真器,端口 102 ports: ["102:102"] profiles: ["dev"] ``` > **说明:** > - `mosquitto` — MQTT Broker,所有 `PublishAsync` / `SubscribeAsync` 需要它 > - `modbus-emulator` — 开发测试用 Modbus TCP 从站模拟器(`docker compose --profile dev up`) > - `s7-emulator` — 开发测试用 S7 PLC 模拟器 > - **SignalR** 不需要额外容器——它是 ASP.NET Core 内置能力,应用启动即就绪 > - Modbus RTU / 真实 PLC 为**物理设备**,Deploy 后连接硬件即可 #### mosquitto.conf(MQTT Broker 最小配置) ``` listener 1883 allow_anonymous true listener 9001 protocol websockets ``` #### 启动命令 ```bash # 启动基础中间件 docker compose up -d # 启动基础中间件 + IoT 模拟器 docker compose --profile dev up -d ``` ## 高可用特性一览 | 模式 | Cache | Consul | Kafka | RabbitMQ | ES | MongoDB | Ocelot | Modbus | S7 | Mqtt | SignalR | |------|:-----:|:------:|:-----:|:--------:|:--:|:-------:|:------:|:------:|:--:|:----:|:-------:| | 重试+指数退避 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | - | ✅ | ✅ | ✅ | - | | 熔断器 | ✅ | - | - | - | - | - | ✅ | - | - | - | - | | 本地缓存降级 | - | ✅ | - | - | - | - | - | - | - | - | - | | 死信队列 | - | - | ✅ | ✅ | - | - | - | - | - | - | - | | 发布确认 | - | - | - | ✅ | - | - | - | - | - | - | - | | 健康检查 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | - | ✅ | ✅ | ✅ | - | | 优雅关闭 | - | ✅ | ✅ | ✅ | - | - | - | - | - | - | - | | 自动重连 | - | - | - | ✅ | - | - | - | ✅ | ✅ | ✅ | ✅ | | 消息缓存 | - | - | - | - | - | - | - | - | - | ✅ | - | | 实时双向通信 | - | - | - | - | - | - | - | - | - | - | ✅ | ## 许可证 MIT License. Copyright (c) 2025 Beauty.Net ## 联系方式 - Gitee: [https://gitee.com/leafCore/beauty-net-web-api](https://gitee.com/leafCore/beauty-net-web-api) - Issues: [https://gitee.com/leafCore/beauty-net-web-api/issues](https://gitee.com/leafCore/beauty-net-web-api/issues) - Email: 154571895@qq.com