关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

云南大王-让 .NET 轻松构建中间件模式代码(二)

发布时间:2020-04-16 00:00:00
让 .NET 轻松构建中间件模式代码(二)--- 支持管道的中断和分支 Intro 上次实现了一个基本的构建中间件模式的中间件构建器,现在来丰富一下功能,让它支持中断和分支,分别对应 asp.net core 中的 applicationBuilder.Run 和 applicationBuilder.MapWhen 实现管道中断 实现中间件的中断其实很简单,通过上一次的分析我们已经知道,中间件每一个部分其实是一个上下文和 next 的委托,只需要忽略 next,不执行 next 就可以了,就可以中断后面中间件的执行。 定义一个 Run 扩展方法来实现方便的实现中间件中断: public static IPipelineBuilder Run(this IPipelineBuilder builder, Action handler) { return builder.Use(_ => handler); } public static IAsyncPipelineBuilder Run(this IAsyncPipelineBuilder builder, Func 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 { IPipelineBuilder Use(Func, Action> middleware); Action Build(); IPipelineBuilder New(); } // public interface IAsyncPipelineBuilder { IAsyncPipelineBuilder Use(Func, Func> middleware); Func Build(); IAsyncPipelineBuilder New(); } 实现就是直接创建了一个新的 PipelineBuilder 对象,示例如下: internal class PipelineBuilder : IPipelineBuilder { private readonly Action _completeFunc; private readonly List, Action>> _pipelines = new List, Action>>(); public PipelineBuilder(Action completeFunc) { _completeFunc = completeFunc; } public IPipelineBuilder Use(Func, Action> middleware) { _pipelines.Add(middleware); return this; } public Action Build() { var request = _completeFunc; for (var i = _pipelines.Count - 1; i >= 0; i--) { var pipeline = _pipelines[i]; request = pipeline(request); } return request; } public IPipelineBuilder New() => new PipelineBuilder(_completeFunc); } 异步的和同步类似,这里就不再赘述,有疑问可以直接看文末的源码链接 接着就可以定义我们的分支扩展了 public static IPipelineBuilder When(this IPipelineBuilder builder, Func predict, Action> 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(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://github.com/WeihanLi/WeihanLi.Common/blob/dev/samples/DotNetCoreSample/PipelineTest.cs https://github.com/WeihanLi/WeihanLi.Common/blob/dev/src/WeihanLi.Common/Helpers/Pipelines/PipelineBuilder.cs https://github.com/dotnet/aspnetcore/blob/master/src/Http/Http/src/Builder/ApplicationBuilder.cs https://github.com/dotnet/aspnetcore/blob/master/src/Http/Http.Abstractions/src/Extensions/MapWhenExtensions.cs https://github.com/dotnet/aspnetcore/blob/master/src/Http/Http.Abstractions/src/Extensions/MapWhenMiddleware.cs

/template/Home/Zkeys/PC/Static