阅读源码探索实现NetCore中间件

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中间件的执行已经完成,感兴趣的小伙伴,可以新建一个测试程序,测试一下,你会发现很多不同的东西哦
 

猜你喜欢

转载自www.cnblogs.com/Qintai/p/11827973.html
今日推荐