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成精了。