让 .NET 轻松构建中间件模式代码(二)
让 .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://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
让 .NET 轻松构建中间件模式代码(二)的更多相关文章
- 让 .NET 轻松构建中间件模式代码
让 .NET 轻松构建中间件模式代码 Intro 在 asp.net core 中中间件的设计令人叹为观止,如此高大上的设计何不集成到自己的代码里呢. 于是就有了封装了一个简单通用的中间件模板的想法, ...
- Java工作流引擎-中间件模式代码集成
关键词:工作流快速开发平台 工作流流设计 业务流程管理 asp.net 开源工作流 bpm工作流系统 java工作流主流框架 自定义工作流引擎 表单设计器 流程设计器 前端代码集成步骤 ...
- ASP.NET Core 1.0中的管道-中间件模式
ASP.NET Core 1.0借鉴了Katana项目的管道设计(Pipeline).日志记录.用户认证.MVC等模块都以中间件(Middleware)的方式注册在管道中.显而易见这样的设计非常松耦合 ...
- Core 1.0中的管道-中间件模式
ASP.NET Core 1.0中的管道-中间件模式 SP.NET Core 1.0借鉴了Katana项目的管道设计(Pipeline).日志记录.用户认证.MVC等模块都以中间件(Middlewar ...
- Java设计模式之《构建者模式》及应用场景
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6553374.html 构建者模式,又称建造者模式,将一部负责对象的构建分为许多小对象的构建 ...
- 深入浅出的webpack构建工具---DevServer配置项(二)
深入浅出的webpack构建工具---DevServer配置项(二) 阅读目录 DevServer配置项 1. contentBase 2. port 3. host 4. headers 5. hi ...
- 深入探索Java设计模式之构建器模式(五)
抽丝剥茧 细说架构那些事——[优锐课] 简单的程序不需要大量的设计过程,因为它们只关注有限的解决方案,仅使用几个类.大型程序专注于广泛的设计,该设计比好的设计范例的任何其他属性都更能利用可重用性.宏伟 ...
- 前端构建大法 Gulp 系列 (二):为什么选择gulp
系列目录 前端构建大法 Gulp 系列 (一):为什么需要前端构建 前端构建大法 Gulp 系列 (二):为什么选择gulp 前端构建大法 Gulp 系列 (三):gulp的4个API 让你成为gul ...
- WCF学习之旅—TCP双工模式(二十一)
WCF学习之旅—请求与答复模式和单向模式(十九) WCF学习之旅—HTTP双工模式(二十) 五.TCP双工模式 上一篇文章中我们学习了HTTP的双工模式,我们今天就学习一下TCP的双工模式. 在一个基 ...
随机推荐
- 建议14:灵活使用Arguments
Javascript支持Arguments机制.Arguments是一个为数组,可以通过数组下标形式获取该集合中传递给函数的参数值.例如:下面函数中,没有指定形参,但在函数体内通过Arguments对 ...
- 【转】分布式架构的演进(JavaWeb)
作者:李小翀 链接:https://www.zhihu.com/question/22764869/answer/31277656 来源:知乎 1.初始 初始阶段 的小型系统 应用程序.数据库.文件等 ...
- Python可以用中文命名
Python中文命名 命名规则数字.字母.下划线 环境 python 3.7.3 x64 win10 现象 Python在命名的地方都是可以用中文命名 如: 变量 中文下划线无法被识别 打出中文下划线 ...
- JAVA多线程面试必看(转载)
JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...
- Fiddler3 使用技巧
1.Fiddler抓不到包怎么解决 (1)先确定是HTTP包抓不到还是HTTPS包抓不到.如果只是HTTPS包抓不到,说明是证书的问题,需要重新安装证书. (2)检查浏览器的HTTP代理设置是否正确, ...
- iOS开发:Swift/Objective-C高效生成随机字符串
原文连接 Objective-C版 // 随机生成字符串(由大小写字母.数字组成) + (NSString *)random: (int)len { char ch[len]; for (int in ...
- System.Text.Json 序列化对所有 JSON 属性名称使用 camel 大小写
asp.net core3.x 新增的序列号接口System.Text.Json 序列化时,如果要对所有 JSON 属性名称使用 camel 大小写 将 JsonSerializerOptions.P ...
- 利用Docker手动构建WebLogic镜像的步骤
info 我的Docker环境信息如下: [root@localhost ~]# docker info -f " OSType: {{.OperatingSystem}} {{.Archi ...
- 【Excel使用技巧】vlookup函数
背景 前不久开发了一个运营小工具,运营人员上传一个id的列表,即可导出对应id的额外数据.需求本身不复杂,很快就开发完了,但上线后,运营反馈了一个问题,导出后的数据跟导出之前的数据顺序不一致. 经过沟 ...
- Redis 按正则获取keys
首先,我被坑了很久由于不知道这个redis支持的正则只有3种 1. * 任意长度的任意字符 2. ? 任意单一字符 3. [xxx] 匹配方括号中的一个字符 2.从上面开来,keys的模糊匹配功能很方 ...