、前言从“作坊”到“工厂”在上一篇文章中我们学会了C#的现代语法就像掌握了制造精密零件的技术。现在我们需要把这些零件组装成一台能运转的发动机。在ASP.NET Core中有两样东西构成了这台发动机的骨架依赖注入DI和中间件。如果你不理解它们你写的代码可能会变成紧紧缠绕的一团乱麻我们称之为“面条代码”难以测试、难以修改。理解了它们你就掌握了现代Web开发的“设计模式之钥”。二、灵魂机制依赖注入DI2.1 为什么要“注入”——解决紧耦合假设你需要在一个API中记录日志。最直观的写法可能是直接在代码里new一个对象app.MapGet(/bad, () { var logger new FileLogger(); // 直接依赖具体的实现类 logger.Log(这是一条日志); return 日志已记录; });这种写法看似简单实则隐患重重紧耦合你的API代码死死地绑定了FileLogger。如果哪天老板说“改成存数据库”你得修改每一处new FileLogger()。难以测试做单元测试时你不想真的去写文件想用一个假的记录器但现在你无法替换。依赖注入的核心思想是“不要自己new需要什么向容器要”控制反转IoC。2.2 服务的三生三世生命周期在.NET的DI容器中注册的服务有三种主要生命周期。这是新手最容易踩坑的地方请务必理解Transient瞬态用完即弃。每次请求该服务容器都会给你一个全新的实例。适合轻量级、无状态的服务如简单的计算器、格式化工具。Scoped范围一次请求一生。在一次HTTP请求范围内无论你在多少个地方请求它拿到的都是同一个实例。这是Web开发中最常用的模式特别是用于数据库上下文DbContext。Singleton单例万世一系。整个应用程序生命周期内只存在一个实例。适合全局缓存、全局配置。注意单例服务必须是线程安全的2.3 实战构建一个性能监控服务我们来写一个真实的案例统计API的执行耗时。第一步定义契约接口良好的架构总是面向接口编程。// IPerformanceTracker.cs public interface IPerformanceTracker { void Start(); void Stop(); long GetElapsedTime(); }第二步实现服务// PerformanceTracker.cs public class PerformanceTracker : IPerformanceTracker { private Stopwatch _stopwatch new Stopwatch(); public void Start() _stopwatch.Restart(); public void Stop() _stopwatch.Stop(); public long GetElapsedTime() _stopwatch.ElapsedMilliseconds; }第三步在Program.cs中注册服务var builder WebApplication.CreateBuilder(args); // --- 注册服务 --- // 这里我们使用 Scoped因为耗时统计通常是针对单个请求的 builder.Services.AddScopedIPerformanceTracker, PerformanceTracker(); // 添加Swagger等基础服务 builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app builder.Build(); // ... 中间件配置 ...第四步在API中注入并使用在Minimal API中我们通过方法参数注入服务。app.MapGet(/test-performance, (IPerformanceTracker tracker) { tracker.Start(); // 模拟耗时操作 Thread.Sleep(500); tracker.Stop(); return $接口执行耗时: {tracker.GetElapsedTime()} ms; });架构师视角的深意 注意看我们的API代码里完全没有new PerformanceTracker()。这意味着如果明天我们需要升级监控逻辑比如加上日志记录我们只需要修改PerformanceTracker.cs类而API接口的代码一行都不用动。这就是解耦带来的维护性提升。三、传动装置中间件管道如果说DI是提供动力的气缸那么中间件就是负责传递动力的齿轮和传送带。3.1 管道模型俄罗斯套娃ASP.NET Core 处理HTTP请求的方式就像水流通过一系列过滤层。请求进入管道。经过一个个中间件。中间件可以在处理前做事如记录请求日志。中间件调用next()将请求传给下一个中间件。到达最终处理逻辑你的API代码。响应沿着管道反向流出。中间件可以在处理后做事如记录响应日志、处理异常。3.2 编写你的第一个自定义中间件我们来写一个最简单的中间件请求计时器。它将在控制台打印每个请求的耗时。var app builder.Build(); // --- 自定义中间件 --- app.Use(async (context, next) { var stopwatch new Stopwatch(); stopwatch.Start(); Console.WriteLine($[中间件] 请求开始: {context.Request.Path}); // 关键步骤调用下一个中间件 // 这里使用 await 等待后续管道全部执行完毕 await next(context); stopwatch.Stop(); Console.WriteLine($[中间件] 请求结束: {context.Request.Path}, 耗时: {stopwatch.ElapsedMilliseconds}ms); }); // 确保有Swagger中间件 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.MapGet(/, () Hello World!); app.Run();运行这段代码并在浏览器访问http://localhost:5000/你会看到控制台输出了耗时信息。3.3 “短路”机制权限守门员中间件有一个极其重要的能力短路。如果中间件决定不调用next()管道就会直接折返后续的逻辑如你的API代码将不会执行。这非常适合做权限验证。app.Use(async (context, next) { // 模拟检查Header里是否有密码 if (!context.Request.Headers.ContainsKey(X-Secret-Key)) { // 没有密钥直接返回401不调用 next() context.Response.StatusCode 401; await context.Response.WriteAsync(抱歉
解构引擎——依赖注入(DI)与中间件管道
、前言从“作坊”到“工厂”在上一篇文章中我们学会了C#的现代语法就像掌握了制造精密零件的技术。现在我们需要把这些零件组装成一台能运转的发动机。在ASP.NET Core中有两样东西构成了这台发动机的骨架依赖注入DI和中间件。如果你不理解它们你写的代码可能会变成紧紧缠绕的一团乱麻我们称之为“面条代码”难以测试、难以修改。理解了它们你就掌握了现代Web开发的“设计模式之钥”。二、灵魂机制依赖注入DI2.1 为什么要“注入”——解决紧耦合假设你需要在一个API中记录日志。最直观的写法可能是直接在代码里new一个对象app.MapGet(/bad, () { var logger new FileLogger(); // 直接依赖具体的实现类 logger.Log(这是一条日志); return 日志已记录; });这种写法看似简单实则隐患重重紧耦合你的API代码死死地绑定了FileLogger。如果哪天老板说“改成存数据库”你得修改每一处new FileLogger()。难以测试做单元测试时你不想真的去写文件想用一个假的记录器但现在你无法替换。依赖注入的核心思想是“不要自己new需要什么向容器要”控制反转IoC。2.2 服务的三生三世生命周期在.NET的DI容器中注册的服务有三种主要生命周期。这是新手最容易踩坑的地方请务必理解Transient瞬态用完即弃。每次请求该服务容器都会给你一个全新的实例。适合轻量级、无状态的服务如简单的计算器、格式化工具。Scoped范围一次请求一生。在一次HTTP请求范围内无论你在多少个地方请求它拿到的都是同一个实例。这是Web开发中最常用的模式特别是用于数据库上下文DbContext。Singleton单例万世一系。整个应用程序生命周期内只存在一个实例。适合全局缓存、全局配置。注意单例服务必须是线程安全的2.3 实战构建一个性能监控服务我们来写一个真实的案例统计API的执行耗时。第一步定义契约接口良好的架构总是面向接口编程。// IPerformanceTracker.cs public interface IPerformanceTracker { void Start(); void Stop(); long GetElapsedTime(); }第二步实现服务// PerformanceTracker.cs public class PerformanceTracker : IPerformanceTracker { private Stopwatch _stopwatch new Stopwatch(); public void Start() _stopwatch.Restart(); public void Stop() _stopwatch.Stop(); public long GetElapsedTime() _stopwatch.ElapsedMilliseconds; }第三步在Program.cs中注册服务var builder WebApplication.CreateBuilder(args); // --- 注册服务 --- // 这里我们使用 Scoped因为耗时统计通常是针对单个请求的 builder.Services.AddScopedIPerformanceTracker, PerformanceTracker(); // 添加Swagger等基础服务 builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app builder.Build(); // ... 中间件配置 ...第四步在API中注入并使用在Minimal API中我们通过方法参数注入服务。app.MapGet(/test-performance, (IPerformanceTracker tracker) { tracker.Start(); // 模拟耗时操作 Thread.Sleep(500); tracker.Stop(); return $接口执行耗时: {tracker.GetElapsedTime()} ms; });架构师视角的深意 注意看我们的API代码里完全没有new PerformanceTracker()。这意味着如果明天我们需要升级监控逻辑比如加上日志记录我们只需要修改PerformanceTracker.cs类而API接口的代码一行都不用动。这就是解耦带来的维护性提升。三、传动装置中间件管道如果说DI是提供动力的气缸那么中间件就是负责传递动力的齿轮和传送带。3.1 管道模型俄罗斯套娃ASP.NET Core 处理HTTP请求的方式就像水流通过一系列过滤层。请求进入管道。经过一个个中间件。中间件可以在处理前做事如记录请求日志。中间件调用next()将请求传给下一个中间件。到达最终处理逻辑你的API代码。响应沿着管道反向流出。中间件可以在处理后做事如记录响应日志、处理异常。3.2 编写你的第一个自定义中间件我们来写一个最简单的中间件请求计时器。它将在控制台打印每个请求的耗时。var app builder.Build(); // --- 自定义中间件 --- app.Use(async (context, next) { var stopwatch new Stopwatch(); stopwatch.Start(); Console.WriteLine($[中间件] 请求开始: {context.Request.Path}); // 关键步骤调用下一个中间件 // 这里使用 await 等待后续管道全部执行完毕 await next(context); stopwatch.Stop(); Console.WriteLine($[中间件] 请求结束: {context.Request.Path}, 耗时: {stopwatch.ElapsedMilliseconds}ms); }); // 确保有Swagger中间件 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.MapGet(/, () Hello World!); app.Run();运行这段代码并在浏览器访问http://localhost:5000/你会看到控制台输出了耗时信息。3.3 “短路”机制权限守门员中间件有一个极其重要的能力短路。如果中间件决定不调用next()管道就会直接折返后续的逻辑如你的API代码将不会执行。这非常适合做权限验证。app.Use(async (context, next) { // 模拟检查Header里是否有密码 if (!context.Request.Headers.ContainsKey(X-Secret-Key)) { // 没有密钥直接返回401不调用 next() context.Response.StatusCode 401; await context.Response.WriteAsync(抱歉