让 .NET 轻松构建中间件模式代码 (二)--- 支持管道的中断和分支
Intro
上次实现了一个基本的构建中间件模式的中间件构建器, 现在来丰富一下功能, 让它支持中断和分支, 分别对应 ASP.NET core 中的 applicationBuilder.Run 和 applicationBuilder.MapWhen
实现管道中断
实现中间件的中断其实很简单, 通过上一次的分析我们已经知道, 中间件每一个部分其实是一个上下文和 next 的委托, 只需要忽略 next, 不执行 next 就可以了, 就可以中断后面中间件的执行.
定义一个 Run 扩展方法来实现方便的实现中间件中断:
- 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 的源码.
实现原理也挺简单的, 其实就是满足分支的条件时创建一个全新的中间件管道, 当满足条件的时候就就执行这个分支中间件管道, 否则就跳过这个分支进入下一个中间件.
首先在 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> 对象, 示例如下:
- 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);
- }
异步的和同步类似, 这里就不再赘述, 有疑问可以直接看文末的源码链接
接着就可以定义我们的分支扩展了
- 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();
- }
- });
- }
使用示例
我们可以使用分支和中断来改造一下昨天的示例, 改造完的示例如下:
- 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 分支也是正确执行的~~
- Reference
- https://www.cnblogs.com/weihanli/p/12700006.html
来源: https://www.cnblogs.com/weihanli/p/12709603.html