ASP.NET Core中间件中HttpContext的注入原理及自定义MyRun方法实现问题
嗨,我来帮你理清这两个问题~
一、HttpContext是怎么被传递的?
在ASP.NET Core的请求处理流程里,每个请求到达时,框架会自动创建一个专属的HttpContext实例,这个实例会伴随整个请求的生命周期。当中间件管道开始处理请求时,框架会负责把这个HttpContext实例依次传递给管道里的每个中间件委托——不管是原生的app.Run、app.Use还是你自定义的扩展方法,都是框架在调度时自动把当前请求的HttpContext传进来的,不需要你手动注入,这是中间件管道的核心运行机制。
简单来说:请求进来→框架创建HttpContext→管道逐个调用中间件→每个中间件的委托都能拿到这个HttpContext。
二、你的自定义MyRun方法哪里出问题了?
看你的代码,问题出在app.Use(handler => handler);这一行,因为IApplicationBuilder.Use方法要求的参数是Func<RequestDelegate, RequestDelegate>,但你直接把MyRequestDelegate类型的handler传进去,类型不匹配,逻辑也不对。
修正后的MyRun扩展方法代码:
public static class MyRunExtension { public static void MyRun(this IApplicationBuilder app, MyRequestDelegate handler) { if (app == null) { throw new ArgumentNullException(nameof(app)); } if (handler == null) { throw new ArgumentNullException(nameof(handler)); } // 适配Use方法的委托格式,Run是终端中间件,无需调用后续中间件 app.Use(next => context => handler(context)); } } // 你的自定义委托和原生RequestDelegate完全一致,也可以直接用RequestDelegate代替 public delegate Task MyRequestDelegate(HttpContext context);
为什么这样改?
app.Use的参数是一个“中间件工厂”委托:它接收下一个中间件的RequestDelegate,返回当前中间件的RequestDelegate。而app.Run的本质是终端中间件——它不会调用后续的中间件,所以我们在实现MyRun时,只需要把自定义的MyRequestDelegate包装成一个忽略next参数的中间件委托即可,这样框架就会把HttpContext传递给你的handler了。
现在你再调用app.MyRun(async context => await context.Response.WriteAsync("hello"));就能得到和原生app.Run一样的效果啦~
备注:内容来源于stack exchange,提问作者S K

