ASP.NET Core 的pipeline中间件

ASP.NET Core 的pipeline中间件

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;

public class  HttpContext                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
{
    public string Request{get;set;}
    public string Response {get;set;}
}

public delegate Task RequestDelegate(HttpContext context); // 委托申明

public interface IApplicationBuilder
{
    IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
    RequestDelegate Build();
 
}

public static class RunExtensions
{
    /// <summary>
    /// Adds a terminal middleware delegate to the application's request pipeline.
    /// </summary>
    /// <param name="app">The <see cref="IApplicationBuilder"/> instance.</param>
    /// <param name="handler">A delegate that handles the request.</param>
    public static void Run(this IApplicationBuilder app, RequestDelegate handler)
    {
        if (app == null)
        {
            throw new ArgumentNullException(nameof(app));
        }

        if (handler == null)
        {
            throw new ArgumentNullException(nameof(handler));
        }

        app.Use(_ => handler);Here "_" represents the argument variable "next". In c# "_" is a valid variable.
    }
}

public static class UseExtensions
{
    /// <summary>
    /// Adds a middleware delegate defined in-line to the application's request pipeline.
    /// </summary>
    /// <param name="app">The <see cref="IApplicationBuilder"/> instance.</param>
    /// <param name="middleware">A function that handles the request or calls the given next function.</param>
    /// <returns>The <see cref="IApplicationBuilder"/> instance.</returns>
    public static IApplicationBuilder Use(this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware)
    {
        return app.Use(next =>
        {
            return context =>
            {
                Func<Task> simpleNext = () => next(context);
                return middleware(context, simpleNext);
            };
        });
    }
}

public class ApplicationBuilder:IApplicationBuilder
{
    private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();
    public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
    {
        _components.Add(middleware);
        return this;
    }

    public RequestDelegate Build()
    {
        // Here is the default middleware in the end of pipeline. Ofcourse it can be bypassed by Run() method.
        RequestDelegate app = async ctx=>{
            System.Console.WriteLine("I am the default(end) middleware.");
            await Task.CompletedTask;
        };
        foreach (var component in _components.Reverse())
        {
            app = component(app);
        }
        return app;
    }
}

class Program
{                                                                                                                                                                                                                                                                                           
    public static void Main()
    {
        IApplicationBuilder appbuilder = new ApplicationBuilder();
        appbuilder.Use(next =>
        {
            return async ctx =>
            {
                System.Console.WriteLine("Enter middleware 1");
                await next(ctx);
                System.Console.WriteLine("Exit middleware 1");
            };
        });
        appbuilder.Use(next =>
        {
            return async ctx =>
            {
                System.Console.WriteLine("Enter middleware 2");
                await next(ctx);
                System.Console.WriteLine("Exit middleware 2");
            };
        });
        appbuilder.Use(next =>
        {
            return async ctx =>
            {
                System.Console.WriteLine("Enter middleware 3");
                await next(ctx);
                System.Console.WriteLine("Exit middleware 3");
            };
        });
        // This Use() is extension method of IApplicationBuilder
        appbuilder.Use(async (context, next) =>
        {
            System.Console.WriteLine("Enter middleware 4");
            await next.Invoke();
            System.Console.WriteLine("Enter middleware 4");
        });
        // This Run() is extension method of IApplicationBuilder
        // The Run() method can add the end middleware. 
        // All the middleware added by Use() after Run() will be bypassed by Run().  
        appbuilder.Run(async ctx=>{
            System.Console.WriteLine("Enter middleware 5");
            await Task.CompletedTask;
            });
        // Get the first middleware.
        RequestDelegate fstapp = appbuilder.Build();
        HttpContext context = new HttpContext(){
            Request = "Request is comming.",
            Response = "Response to you."
        };
        // Run the first middleware will run all the middleware in the pipeline.
        fstapp(context);
        Console.Read();
    }
}

/*
Enter middleware 1
Enter middleware 2
Enter middleware 3
Enter middleware 4
Enter middleware 5
Enter middleware 4
Exit middleware 3
Exit middleware 2
Exit middleware 1
*/

上面的代码假设注释了122-135行,结果如下:

/*
Enter middleware 1
Enter middleware 2
Enter middleware 3
I am the default(end) middleware.
Exit middleware 3
Exit middleware 2
Exit middleware 1
*/

为了说明ASP.NET Core中pipeline的结构,我们模拟了上面这么一个pipeline和middleware的代码。

代码模拟了HttpContext类(实际ASP.NET Core中这个类是abstract类,这里为了模拟,做了很多简化),IApplicationBuilder, ApplicationBuilder,RequestDelegate类。

这段代码参考了实际的实现,真实模拟了中间件的建构过程,使用List类将delegate委托巧妙的形成了链表。

代码中自定义了两个委托:RequestDelegate和Func<RequestDelegate,RequestDelegate>。

我们称RequestDelegate是"具体的中间件",说它是具体的中间件,是因为其实它就是一个具体的方法。

Func<T1,T2>是.NET自带的的泛型委托类型,它代表一个类似如下的方法:

RequestDelegate function(RequestDelegate)

也就是这个方法参数是RequestDelegate,返回值也是一个RequestDelegate。参数的RequestDelegate代表的是要执行的"下一个中间件",返回的RequestDelegate则表示了"当前中间件"。

Func<RequestDelegate,RequestDelegate>这委托就是为了让"当前中间件"调用"下一个中间件",也就是利用Func<RequestDelegate,RequestDelegate>委托可以实现一个中间件调用"下一个中间件";

我们称Func<RequestDelegate,RequestDelegate>为一个"pipeline单元";

ApplicationBuilder中包含一个List列表_compoments,这个列表中每个元素是一个Func<RequestDelegate,RequestDelegate>委托。

ApplicationBuilder的Use()方法就是向_compoments链表中增加一个形式为Func<RequestDelegate,RequestDelegate>的"中间件链接单元",也就是插入一个一个Func<RequestDelegate,RequestDelegate>方法。代码中这些方法都是以匿名形式给出的,但是即使是匿名方法,在内存中也会真实的生成这些方法,注意此时具体的RequestDelegate方法并没有生成,如下图所示(这里的图是假设注释了122-135行的代码):

上图中蓝色部分表示List链表,黑色的部分表示一个个Func<RequestDelegate,RequestDelegate>方法,调动完Use后这些匿名方法就在内存中存在了,只不过还没有调用它们。

当我们调用Build()方法的时候,Build()方法首先会生成一个默认的RequestDelegate中间件(真实的ASP.NET Core同样会生成一个默认的中间件,不过比我们给的例子复杂一些),这个默认的中间件是会链接到pipeline尾部,作为最后一个中间件,明显最后的中间件是不需要next中间件的。接下来,Build()会倒序执行链表中的Func<RequestDelegate,RequestDelegate>方法,执行这些方法的目的是为了在内存中生成一个一个的RequestDelegate方法,因为这些方法才是具体需要的中间件,并且通过Func<RequestDelegate,RequestDelegate>方法中的next参数具体化每个RequestDelegate方法(显然,这里的RequestDelegate方法也都是匿名方法):

调用完Build()方法后,内存中就出现了如上图的一个个链接好的RequestDelegate方法。

至此,pipeline及整个中间件都实现了。

最后,调用第一个middleware委托实现一连串中间件的调用。

应该说微软使用Delegate成精了。

发布了108 篇原创文章 · 获赞 126 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/qq_16587307/article/details/104301877