上次实现了一个基本的构建中间件模式的中间件构建器,如今来丰富一下功能,让它支持中断和分支,分别对应 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
分支也是正确执行的~~