上次實現了一個基本的構建中間件模式的中間件構建器,如今來豐富一下功能,讓它支持中斷和分支,分別對應 asp.net core 中的 applicationBuilder.Run
和 applicationBuilder.MapWhen
html
實現中間件的中斷其實很簡單,經過上一次的分析咱們已經知道,中間件每個部分實際上是一個上下文和 next
的委託,只須要忽略 next
,不執行 next
就能夠了,就能夠中斷後面中間件的執行。git
定義一個 Run
擴展方法來實現方便的實現中間件中斷:github
public static IPipelineBuilder<TContext> Run<TContext>(this IPipelineBuilder<TContext> builder, Action<TContext> handler) { return builder.Use(_ => handler); } public static IAsyncPipelineBuilder<TContext> Run<TContext>(this IAsyncPipelineBuilder<TContext> builder, Func<TContext, Task> handler) { return builder.Use(_ => handler); }
分支的實現主要是參考 asp.net core 裏 applicationBuilder.Map
/applicationBuilder.MapWhen
實現分支路由的作法,在 asp.net core 裏,MapWhen
是一個擴展方法,其實現是一個 MapWhenMiddleware
,有興趣能夠看 asp.net core 的源碼。app
實現原理也挺簡單的,其實就是知足分支的條件時建立一個全新的中間件管道,當知足條件的時候就就執行這個分支中間件管道,不然就跳過這個分支進入下一個中間件。asp.net
首先在 PipelineBuilder
的接口定義中增長了一個 New
方法用來建立一個全新的中間件管道,定義以下:異步
public interface IPipelineBuilder<TContext> { IPipelineBuilder<TContext> Use(Func<Action<TContext>, Action<TContext>> middleware); Action<TContext> Build(); IPipelineBuilder<TContext> New(); } // public interface IAsyncPipelineBuilder<TContext> { IAsyncPipelineBuilder<TContext> Use(Func<Func<TContext, Task>, Func<TContext, Task>> middleware); Func<TContext, Task> Build(); IAsyncPipelineBuilder<TContext> New(); }
實現就是直接建立了一個新的 PipelineBuilder<TContext>
對象,示例以下:ui
internal class PipelineBuilder<TContext> : IPipelineBuilder<TContext> { private readonly Action<TContext> _completeFunc; private readonly List<Func<Action<TContext>, Action<TContext>>> _pipelines = new List<Func<Action<TContext>, Action<TContext>>>(); public PipelineBuilder(Action<TContext> completeFunc) { _completeFunc = completeFunc; } public IPipelineBuilder<TContext> Use(Func<Action<TContext>, Action<TContext>> middleware) { _pipelines.Add(middleware); return this; } public Action<TContext> Build() { var request = _completeFunc; for (var i = _pipelines.Count - 1; i >= 0; i--) { var pipeline = _pipelines[i]; request = pipeline(request); } return request; } public IPipelineBuilder<TContext> New() => new PipelineBuilder<TContext>(_completeFunc); }
異步的和同步相似,這裏就再也不贅述,有疑問能夠直接看文末的源碼連接this
接着就能夠定義咱們的分支擴展了.net
public static IPipelineBuilder<TContext> When<TContext>(this IPipelineBuilder<TContext> builder, Func<TContext, bool> predict, Action<IPipelineBuilder<TContext>> configureAction) { return builder.Use((context, next) => { if (predict.Invoke(context)) { var branchPipelineBuilder = builder.New(); configureAction(branchPipelineBuilder); var branchPipeline = branchPipelineBuilder.Build(); branchPipeline.Invoke(context); } else { next(); } }); }
咱們可使用分支和中斷來改造一下昨天的示例,改造完的示例以下:code
var requestContext = new RequestContext() { RequesterName = "Kangkang", Hour = 12, }; var builder = PipelineBuilder.Create<RequestContext>(context => { Console.WriteLine($"{context.RequesterName} {context.Hour}h apply failed"); }) .When(context => context.Hour <= 2, pipeline => { pipeline.Use((context, next) => { Console.WriteLine("This should be invoked"); next(); }); pipeline.Run(context => Console.WriteLine("pass 1")); pipeline.Use((context, next) => { Console.WriteLine("This should not be invoked"); next(); Console.WriteLine("will this invoke?"); }); }) .When(context => context.Hour <= 4, pipeline => { pipeline.Run(context => Console.WriteLine("pass 2")); }) .When(context => context.Hour <= 6, pipeline => { pipeline.Run(context => Console.WriteLine("pass 3")); }) ; var requestPipeline = builder.Build(); Console.WriteLine(); foreach (var i in Enumerable.Range(1, 8)) { Console.WriteLine($"--------- h:{i} apply Pipeline------------------"); requestContext.Hour = i; requestPipeline.Invoke(requestContext); Console.WriteLine("----------------------------"); }
輸出結果以下:
看輸出結果咱們能夠看到 Run
後面註冊的中間件是不會執行的,Run
前面註冊的中間件正常執行
而後定義的 When
分支也是正確執行的~~