C#怎么创建中间件管道_C# ASP.NET Core自定义中间件教程【进阶】
C#怎么创建中间件管道_C# ASP.NET Core自定义中间件教程【进阶】 说到中间件管道,新手常有个误解,以为它是“创建”出来的一个独立对象。其实不然,管道更像是一条由你亲手组装的流水线,通过 app.Use()、app.UseMiddleware() 和 app.Map() 这些方法,按注册
C#怎么创建中间件管道_C# ASP.NET Core自定义中间件教程【进阶】

说到中间件管道,新手常有个误解,以为它是“创建”出来的一个独立对象。其实不然,管道更像是一条由你亲手组装的流水线,通过 app.Use()、app.UseMiddleware() 和 app.Map() 这些方法,按注册顺序拼接成一条链式的委托流。这里有个关键点:顺序错了、漏了 await next()、或者在不该停下的地方提前返回,管道就会“静默”断裂——系统通常不会报错,但后续所有逻辑都失效了,调试起来相当棘手。
中间件注册顺序决定执行逻辑,不是可选项
ASP.NET Core 框架本身可不会帮你检查注册顺序是否合理,它只会忠实地、机械地按照你写的代码顺序来执行。这就意味着,顺序本身就是逻辑的一部分。来看几个典型的顺序陷阱:
- 身份验证 (
app.UseAuthentication()) 必须放在授权 (app.UseAuthorization()) 之前。否则,当授权中间件检查User.Identity.IsAuthenticated时,它拿到的还是false,授权自然会直接失败。 - 异常处理中间件 (
app.UseExceptionHandler()) 需要放在所有可能抛出异常的中间件之前,比如自定义日志中间件或者JWT验证中间件。放晚了,异常就“溜”过去了,根本捕获不到。 - 静态文件中间件 (
app.UseStaticFiles()) 如果放得太靠后,请求可能先被MVC或API中间件拦截并处理了,文件压根没有机会被查找到。
函数式中间件 vs 类式中间件:什么时候该用哪个
选择哪种写法,不是看哪个“更高级”,而是取决于实际场景。函数式写法胜在简洁直观,适合那些一次性、无外部依赖、逻辑简单的轻量级任务。而类式写法则能无缝接入依赖注入容器,享受构造器注入和生命周期管理,适合更复杂、有状态的场景。
- 函数式:像这样
app.Use(async (context, next) => { await context.Response.WriteAsync("hi"); await next(); });。优点一目了然:没有状态管理,不依赖外部服务,调试时所有逻辑尽收眼底。 - 类式:必须遵循固定模式。构造函数需要接收一个
RequestDelegate类型的next参数,并且必须包含一个名为InvokeAsync的公共方法,否则UseMiddleware扩展方法会找不到入口。() - 还有一个容易踩的坑:类式中间件必须在服务容器中注册(例如
services.AddTransient)。如果忘了这一步,() UseMiddleware在尝试解析依赖时会直接抛出InvalidOperationException。
为什么 await next() 漏了就“没反应”
中间件默认是不会自动调用下一个组件的。漏掉 await next(),就等于你在这条流水线上按下了停止键,后续所有中间件(包括最终的终结点)都不会被执行。更麻烦的是,HTTP响应可能已经部分写出,导致客户端要么一直卡住等待,要么收到一个不完整的响应体。
- 典型错误:写成
next();(漏了await)。代码能编译通过,但因为next返回的是Task,不等待它完成就等同于跳过了后续调用,效果和主动短路一样。 - 调试小技巧:在中间件的开头和结尾分别加上日志输出,比如
Console.WriteLine($"[{nameof(CustomMiddleware)}] enter")和Console.WriteLine($"[{nameof(CustomMiddleware)}] exit")。运行时观察日志,如果只看到“enter”而没有对应的“exit”,那基本可以断定是await next()被跳过了、中间有未捕获的异常,或者代码提前return了。
Map 分支管道里,next 的含义变了
当你使用 app.Map("/api", branch => { ... }) 创建分支时,事情变得有点不一样。这里的 branch 参数是一个独立的 IApplicationBuilder,你在它内部通过 Use 注册的中间件,构成的是一个全新的子管道。此时,子管道里的 next 指向的是这个分支内的下一个中间件,而不是主管道里排在 Map 后面的那些。
- 在分支内部调用
next(),请求不会神奇地回到主管道继续执行。主管道的执行在进入Map匹配成功后就已经暂停了。 - 如果分支管道末尾没有用
Run()明确终止,或者没有匹配到任何终结点,请求就会“掉出”分支。但请注意,它不会自动回到主管道,而是直接结束,通常返回一个404状态码。 - 那么,有没有办法让分支处理完再回到主流程?答案是:不行。
Map的设计就是隔离的。如果真有跨分支和主管道的复用逻辑,正确的做法是将其抽离成共享的服务,而不是指望管道能自动穿透。
话说回来,真正考验开发者的,往往不是中间件本身的编写,而是两个关键判断:「这个业务逻辑到底该不该放在中间件里?」以及「如果该放,它应该插在管道的哪个位置?」很多看似“通用”的需求,比如统一的响应格式包装,其实放在控制器基类、Action过滤器或者像 IApiResponseFormatter 这类更贴近业务层的扩展点上会更合适。中间件位于请求处理管道的底层,一旦写错或设计不当,影响的是流经它的每一个请求,排查起来反而更费周章。
Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。
极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。
















