MoneyTree.Web 1.0.6
MoneyTree.Web Web 集成
📋 概述
MoneyTree.Web 是框架的 Web 集成模块,统一所有模块的 Web 入口,提供静态启动方法(MoneyTreeHost)、全局异常处理、请求日志、ApiResult 自动包装、CurrentUser 自动填充、SSE 支持等中间件和过滤器,并以 NLog 作为默认日志组件。
| 属性 | 说明 |
|---|---|
| NuGet 包 | MoneyTree.Web |
| 外部依赖 | MoneyTree.Core、ASP.NET Core、NLog.Web.AspNetCore |
| 定位 | 统一 Web 入口与中间件集成 |
🏗️ 项目文件结构
MoneyTree.Web/
├── MoneyTree.Web.csproj
├── GlobalUsings.cs
├── Host/
│ └── MoneyTreeHost.cs # 静态启动入口
├── Middlewares/
│ ├── GlobalExceptionMiddleware.cs # 全局异常处理
│ ├── HmacAuthMiddleware.cs # HMAC 认证中间件
│ └── RequestLoggingMiddleware.cs # 请求日志记录
├── Extensions/
│ ├── ClaimsPrincipalExtensions.cs # ClaimsPrincipal 扩展
│ ├── HmacHttpClientExtensions.cs # HMAC HttpClient 扩展
│ ├── SseExtensions.cs # SSE 流式响应扩展
│ └── WebDefaultsExtensions.cs # 默认 Web 配置扩展
├── Filters/
│ └── ApiResultFilter.cs # ApiResult 自动包装过滤器
├── HttpHandlers/
│ └── HmacAuthHandler.cs # HMAC 签名请求处理器
├── Options/
│ └── HmacAuthOptions.cs # HMAC 认证配置选项
└── ProblemDetails/
└── ProblemDetailsFactory.cs # 标准 Problem Details 工厂
🎯 核心能力
| 能力 | 实现 | 说明 |
|---|---|---|
| NLog 默认日志 | 预置 NLog 配置 | 自动集成,输出到控制台和文件 |
| 全局异常处理 | GlobalExceptionMiddleware |
自动转换为 ApiResult 格式 |
| 请求日志 | RequestLoggingMiddleware |
自动记录请求耗时、状态码 |
| ApiResult 自动包装 | ApiResultFilter |
响应自动包装为 ApiResult 格式 |
| CurrentUser 填充 | ClaimsPrincipalExtensions |
通过 ClaimsPrincipal 扩展方法获取当前用户 |
| SSE 支持 | SseExtensions |
IAsyncEnumerable 转 text/event-stream |
| SignalR 兼容 | 原生兼容 | 中间件管道与 SignalR 原生兼容 |
| 健康检查 | 内置端点 | /health(开发)+ /alive(所有环境),K8s liveness probe |
| 双路径启动 | Run() / RunAdvanced() |
默认 Aspire 原生路径 / 专家 MoneyTree 模块路径 |
| 服务默认配置 | AddMoneyTreeServiceDefaults |
弹性 + 遥测 + 服务发现一键注入 |
🚀 静态 Program 入口
MoneyTreeHost 提供两种启动模式:
| 方法 | 路径 | 注入内容 |
|---|---|---|
Run() |
默认,Aspire 原生 | OpenTelemetry + ServiceDiscovery + Resilience + 健康检查 |
Run(configureDefaults) |
高级,MoneyTree 模块 | 弹性 + 可选 MoneyTree.Telemetry/ServiceDiscovery |
默认启动(推荐)
// 自动走 Aspire 原生路径,无需传 configureDefaults
MoneyTreeHost.Run(args, builder =>
{
builder.Services.AddDbContext<MyDbContext>(...);
}, (builder, app) =>
{
app.UseHmacAuth();
});
高级模块路径(精细控制)
MoneyTreeHost.Run(args, builder =>
{
builder.Services.AddDbContext<MyDbContext>(...);
}, (builder, app) =>
{
app.UseHmacAuth();
},
configureDefaults: d =>
{
d.Resilience = r => r.WithRetry(3, 1).WithTimeout(30);
d.Telemetry = t => t.WithServiceName("MyService").UseOpenTelemetryCollector();
d.ServiceDiscovery = sd => sd.WithServiceName("MyService").UseConsul();
});
专家 API(不注入 ServiceDefaults)
// RunAdvanced 完全手动控制,不调用 AddMoneyTreeServiceDefaults
MoneyTreeHost.RunAdvanced(args, builder => { ... }, (builder, app) => { ... });
方式四:自定义配置类(推荐生产环境)
public class AppConfig : IMoneyTreeAppConfig
{
public void ConfigureDatabase(DatabaseOptions db)
{
db.UsePostgreSql("Host=localhost;Database=mydb");
}
public void ConfigureMultiTenancy(MultiTenancyOptions mt)
{
mt.UseHeader("X-Tenant-Id");
}
public void ConfigureCaching(CachingOptions cache)
{
cache.UseRedis("localhost:6379");
}
public void ConfigureTelemetry(TelemetryOptions telemetry)
{
telemetry.UseOpenTelemetryCollector();
}
public void ConfigureResilience(ResilienceOptions resilience)
{
resilience.Defaults.SetRetry(3);
}
}
// Program.cs — 一行启动
MoneyTreeHost.Run<AppConfig>();
方式五:带参数解析
// 自动解析命令行参数或环境变量
MoneyTreeHost.Run(args, app =>
{
var connectionString = app.Configuration.GetConnectionString("Default");
app.UsePostgreSql(connectionString!);
app.UseRedis(app.Configuration["Redis:ConnectionString"]!);
});
📋 IMoneyTreeAppConfig 接口
| 方法 | 说明 |
|---|---|
ConfigureDatabase(DatabaseOptions db) |
数据库配置,可选实现 |
ConfigureMultiTenancy(MultiTenancyOptions mt) |
多租户配置,可选实现 |
ConfigureCaching(CachingOptions cache) |
缓存配置,可选实现 |
ConfigureTelemetry(TelemetryOptions telemetry) |
遥测配置,可选实现 |
ConfigureResilience(ResilienceOptions resilience) |
弹性配置,可选实现 |
ConfigureApiVersioning(ApiVersioningOptions versioning) |
API 版本配置,可选实现 |
ConfigureCors(CorsOptions cors) |
跨域配置,可选实现 |
ConfigureServices(IServiceCollection services) |
自定义服务注册 |
ConfigureMiddleware(IApplicationBuilder app) |
自定义中间件 |
业务系统只需实现关心的配置方法,其余使用框架默认值。
📖 使用示例
配置服务和中间件
// Program.cs
MoneyTreeHost.Run(args,
configureBuilder: builder =>
{
builder.Services.AddScoped<IMyService, MyService>();
},
configureApp: (builder, app) =>
{
app.UseMiddleware<MyMiddleware>();
});
抛出业务异常
在服务中抛出的异常自动被 GlobalExceptionMiddleware 捕获并转换为 ApiResult 格式:
public class OrderService
{
public async Task<Order> GetAsync(string id)
{
var order = await _db.Orders.FindAsync(id);
return order ?? throw new NotFoundException("订单", id);
}
public async Task CancelAsync(string id)
{
var order = await _db.Orders.FindAsync(id);
if (order.Status == OrderStatus.Shipped)
{
throw new DomainException("已发货的订单无法取消", 409, "ORDER_SHIPPED");
}
}
}
获取当前用户
在 Controller 或 Minimal API 中:
app.MapGet("/api/me", (ClaimsPrincipal user) =>
{
var currentUser = user.ToCurrentUser();
return Results.Ok(new { currentUser.Id, currentUser.UserName });
});
在服务中使用(通过 IHttpContextAccessor):
public class OrderService
{
private readonly IHttpContextAccessor _httpContextAccessor;
private ICurrentUser CurrentUser =>
_httpContextAccessor.HttpContext?.User.ToCurrentUser()
?? CurrentUser.Core.CurrentUser.Anonymous;
}
SSE 终结点
app.MapGet("/api/notifications", (HttpResponse response) =>
{
var stream = GetNotificationStream();
return stream.ToSseResult("notification");
});
📝 NLog 默认配置
框架预置 nlog.config,AddMoneyTreeDefaults() 自动注册 NLog:
<nlog>
<targets>
<target name="console" xsi:type="Console" />
<target name="file" xsi:type="File"
fileName="logs/${shortdate}.log"
layout="${longdate}|${level}|${logger}|${message}|${exception:format=tostring}" />
</targets>
<rules>
<logger name="Microsoft.*" maxLevel="Warn" final="true" />
<logger name="*" minLevel="Info" writeTo="console,file" />
</rules>
</nlog>
| NLog 集成特性 | 说明 |
|---|---|
| 自动配置 | 框架内置默认 nlog.config,开箱即用 |
| 结构化日志 | 自动注入 TraceId、TenantId、UserId |
| Telemetry 联动 | 日志与 OpenTelemetry Traces 自动关联 |
Hmac使用示例
方式一:直接指定密钥
// Program.cs builder.Services.AddHttpClient("PaymentService", client => { client.BaseAddress = new Uri("https://payment.example.com"); }) .AddHmacAuthHandler("order-service", "your-secret-key");
方式二:从配置中心读取密钥
// Program.cs builder.Services.AddHttpClient("NotificationService", client => { client.BaseAddress = new Uri("https://notification.example.com"); }) .AddHmacAuthHandler("order-service"); // 自动从 IConfiguration 读取密钥
方式三:在服务中使用
public class OrderService { private readonly IHttpClientFactory _httpClientFactory;
public OrderService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task CreatePaymentAsync(CreatePaymentRequest request)
{
var client = _httpClientFactory.CreateClient("PaymentService");
// 请求自动携带 HMAC 签名头,无需手动处理
var response = await client.PostAsJsonAsync("/api/payment/create", request);
}
}
No packages depend on MoneyTree.Web.
.NET 10.0
- MoneyTree.Core (>= 1.0.4)
- MoneyTree.DependencyInjection (>= 1.0.4)
- MoneyTree.ServiceDefaults (>= 1.0.1)
- NLog.Web.AspNetCore (>= 6.1.4)