天天看點

ASP.NET Core 的pipeline中間件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行的代碼):

ASP.NET Core 的pipeline中間件ASP.NET Core 的pipeline中間件

上圖中藍色部分表示List連結清單,黑色的部分表示一個個Func<RequestDelegate,RequestDelegate>方法,調動完Use後這些匿名方法就在記憶體中存在了,隻不過還沒有調用它們。

當我們調用Build()方法的時候,Build()方法首先會生成一個預設的RequestDelegate中間件(真實的ASP.NET Core同樣會生成一個預設的中間件,不過比我們給的例子複雜一些),這個預設的中間件是會連結到pipeline尾部,作為最後一個中間件,明顯最後的中間件是不需要next中間件的。接下來,Build()會倒序執行連結清單中的Func<RequestDelegate,RequestDelegate>方法,執行這些方法的目的是為了在記憶體中生成一個一個的RequestDelegate方法,因為這些方法才是具體需要的中間件,并且通過Func<RequestDelegate,RequestDelegate>方法中的next參數具體化每個RequestDelegate方法(顯然,這裡的RequestDelegate方法也都是匿名方法):

ASP.NET Core 的pipeline中間件ASP.NET Core 的pipeline中間件

調用完Build()方法後,記憶體中就出現了如上圖的一個個連結好的RequestDelegate方法。

至此,pipeline及整個中間件都實作了。

最後,調用第一個middleware委托實作一連串中間件的調用。

應該說微軟使用Delegate成精了。

繼續閱讀