1.先看代码,在Core2.2 Startup 中,Configure 方法写入中间件,app.Use
1 public void Configure(IApplicationBuilder app, IHostingEnvironment env) 2 { 3 #region Middleware 4 app.Use(next => 5 { 6 Console.WriteLine("this is Middleware1"); 7 return new RequestDelegate(async context => 8 { 9 context.Response.ContentType = "text/html; charset=utf-8"; 10 await context.Response.WriteAsync("<span><h3>This is Middleware1 start</h3></span>"); 11 await next.Invoke(context); 12 await context.Response.WriteAsync("<span><h3>This is Middleware1 end</h3></span>"); 13 }); 14 }); 15 app.Use(next => 16 { 17 return new RequestDelegate(async context => 18 { 19 await context.Response.WriteAsync("<span><h3>This is Middleware2 start</h3></span>"); 20 await next.Invoke(context); 21 await context.Response.WriteAsync("<span><h3>This is Middleware2 end</h3></span>"); 22 }); 23 }); 24 app.Use(next => 25 { 26 Console.WriteLine("this is Middleware3"); 27 return new RequestDelegate(async context => 28 { 29 await context.Response.WriteAsync("<span><h3>This is Middleware3 start</h3></span>"); 30 await next.Invoke(context); 31 await context.Response.WriteAsync("<span><h3>This is Middleware3 end</h3></span>"); 32 }); 33 }); 34 app.Run(async context => await context.Response.WriteAsync("跑完了.")); //这一句得加上,才能看出 35 #endregion 36 37 38 39 if (env.IsDevelopment()) 40 app.UseDeveloperExceptionPage(); 41 else 42 { 43 app.UseExceptionHandler("/Home/Error"); 44 // The default HSTS value is 30 days. You may want to change this for production scenarios, see 45 app.UseHsts(); 46 } 47 48 app.UseHttpsRedirection(); 49 app.UseStaticFiles(); 50 app.UseCookiePolicy(); 51 52 app.UseMvc(routes => 53 { 54 routes.MapRoute( 55 name: "default", 56 template: "{controller=Home}/{action=Index}/{id?}"); 57 }); 58 }
2.调试运行输出
3.通过查看源码,得知app.Use方法其实去添加了一个Func委托到 IList<Func<RequestDelegate, RequestDelegate>>
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware) { //_components 就是这个 private readonly IList<Func<RequestDelegate, RequestDelegate>> _components //= new List<Func<RequestDelegate, RequestDelegate>>(); _components.Add(middleware);// _components return this; }
处理中间件的逻辑看这段代码
1 public RequestDelegate Build() 2 { 3 RequestDelegate app = context => 4 { 5 // If we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened. 6 // This could happen if user code sets an endpoint, but they forgot to add the UseEndpoint middleware. 7 var endpoint = context.GetEndpoint(); 8 var endpointRequestDelegate = endpoint?.RequestDelegate; 9 if (endpointRequestDelegate != null) 10 { 11 var message = 12 $"The request reached the end of the pipeline without executing the endpoint: '{endpoint.DisplayName}'. " + 13 $"Please register the EndpointMiddleware using '{nameof(IApplicationBuilder)}.UseEndpoints(...)' if using " + 14 $"routing."; 15 throw new InvalidOperationException(message); 16 } 17 18 context.Response.StatusCode = 404; 19 return Task.CompletedTask; 20 }; 21 22 foreach (var component in _components.Reverse()) 23 { 24 app = component(app); 25 } 26 27 return app; 28 }
透过这段源码可以发现,处理整个中间件逻辑是,所有的中间件 被存入到 集合 _components ,核心就是把这个集合,转换成单个委托 RequestDelege 。方便调用者调用,执行委托里面的 方法。
借助于这个想法和思路,自己来写个demo
新建一个Core测试程序:
namespace Test { /// <summary> /// 给入字符串,返回 Task /// </summary> /// <param name="str"></param> /// <returns></returns> public delegate Task Conduit(string str); public class MiddlewareDemo { [Test] public void Implement() { CustomMiddleware middle = new CustomMiddleware(); #region C middle.Add(next => { return zfc => { Console.WriteLine("第一阶段开始"); System.Diagnostics.Debug.WriteLine("第一阶段开始"); return next(zfc); }; }); middle.Add(next => { return zfc => { Console.WriteLine("第二阶段开始"); System.Diagnostics.Debug.WriteLine("第二阶段开始"); return next(zfc); }; }); middle.Add(next => { return zfc => { Console.WriteLine("第三阶段开始"); System.Diagnostics.Debug.WriteLine("第三阶段开始"); return next(zfc); }; }); #endregion #region D //middle.Add(next => //{ // return async zfc => // { // Console.WriteLine("第一阶段开始"); // System.Diagnostics.Debug.WriteLine("第一阶段开始"); // await next(zfc); // Console.WriteLine("第一阶段结束"); // System.Diagnostics.Debug.WriteLine("第一阶段结束"); // }; //}); //middle.Add(next => //{ // return async zfc => // { // Console.WriteLine("第二阶段开始"); // System.Diagnostics.Debug.WriteLine("第二阶段开始"); // await next(zfc); // System.Diagnostics.Debug.WriteLine("第二阶段结束"); // Console.WriteLine("第二阶段结束"); // }; //}); //middle.Add(next => //{ // return async zfc => // { // Console.WriteLine("第三阶段开始"); // System.Diagnostics.Debug.WriteLine("第三阶段开始"); // await next(zfc); // System.Diagnostics.Debug.WriteLine("第三阶段结束"); // Console.WriteLine("第三阶段结束"); // }; //}); #endregion var ak = middle.GetDelegate(); ak.Invoke("正序执行中间件"); System.Diagnostics.Debug.WriteLine("**********分界线**********"); var ff = middle.Reverse(); ff.Invoke("开始执行中间件!"); System.Diagnostics.Debug.WriteLine("跑完了ddd"); Console.WriteLine("跑完了ddd"); } } /// <summary> /// 模拟中间件 /// </summary> public class CustomMiddleware { public IList<Func<Conduit, Conduit>> _middlelist = new List<Func<Conduit, Conduit>>(); // Add也就是NetCore中StartUp中的 app.Use ,同理 public void Add(Func<Conduit, Conduit> func) { _middlelist.Add(func); } //为了方便理解做比较,这个没有集合倒序 //把集合整合成一个 Conduit委托 public Conduit GetDelegate() { Conduit conduit = str => { str = "初始化!"; return Task.CompletedTask; }; foreach (Func<Conduit, Conduit> item in _middlelist)//正序 //核心在这里,看似在赋新值,其实是在一层又一层包裹这一个执行方法实例, //给人感觉就像在叠加一样,如果这个地方没有倒序,那么执行方法顺序就是反的 conduit = item(conduit); return conduit; } /// <summary> /// 把集合进行倒叙,然后整合成一个 Conduit委托 /// </summary> /// <returns></returns> public Conduit Reverse() { Conduit app = str => { str = "初始化!"; return Task.CompletedTask; }; foreach (var component in _middlelist.Reverse())//倒序 { app = component(app); } return app; } /* public RequestDelegate Build() { IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>(); RequestDelegate app = context => { // If we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened. // This could happen if user code sets an endpoint, but they forgot to add the UseEndpoint middleware. var endpoint = context.GetEndpoint(); var endpointRequestDelegate = endpoint?.RequestDelegate; if (endpointRequestDelegate != null) { var message = $"The request reached the end of the pipeline without executing the endpoint: '{endpoint.DisplayName}'. " + $"Please register the EndpointMiddleware using '{nameof(IApplicationBuilder)}.UseEndpoints(...)' if using " + $"routing."; throw new InvalidOperationException(message); } context.Response.StatusCode = 404; return Task.CompletedTask; }; foreach (var component in _components.Reverse()) { app = component(app); } return app; } */ } }
运行效果:
至此,模仿Core中间件的执行已经完成,感兴趣的小伙伴,可以新建一个测试程序,测试一下,你会发现很多不同的东西哦