让 .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的双工模式. 在一个基 ...
随机推荐
- 面向对象里is-a和has-a的含义
面向对象的核心思想是:抽象.封装.继承.多态.在实践中用的最多的术语就是 is a(是一个) ,和 has a(有一个).其实他们的意思很简单,对应面向对象设计中的两种形态继承.组合. 一.继承( i ...
- IntelliJ IDEA 常用快捷键(转载)
转自:http://www.open-open.com/lib/view/open1396578860887.html Ctrl+Shift + Enter,语句完成“!”,否定完成,输入表达式时按 ...
- Natas4 Writeup(Referer篡改)
Natas4: 提示来源出错,合法用户只能来自"http://natas5.natas.labs.overthewire.org/". 在http的header中,referer代 ...
- 【盘它!】那些让效率MAX的工具和方法(Mac篇)
一.前言 人类之所以伟大,是因为会创造并使用工具! 工欲善其事必先利器.高效的工具和方法不仅能最大化 节省我们的时间,还可以一定程度上让我们 保持专注,以达到 事半功倍 的效果. 但仅仅有工具不会使用 ...
- CAS无锁模式
一.java内存模型:JMM 在内存模型当中定义一个主内存,所有声明的实例变量都存在于主内存当中,主内存的数据会共享给所有线程,每一个线程有一个块工作内存,工作内存当中主内存数据的副本当更新数据时,会 ...
- Loadrunner 11安装和破解
一.安装环境和文件准备 1.操作系统:Windows Server 2008 R2 Enterprise: 2.loadrunner版本:loadrunner 11: 3.安装浏览器:火狐39.0: ...
- 控制台报错Cause: org.xml.sax.SAXParseException; lineNumber: 4; columnNumber: 78; 元素类型 "select" 必须后跟属性规范 ">" 或 "/>"
首先我的控制台报错是这样的,我找了一下原因看到是第四行的错误: 它说元素select后面必须跟属性规范">""/>"但是我把我眼睛都快丑瞎了都没发现 ...
- Ruby使用记录
1.首先,重要的事情说三遍,不用在Windows里开发Ruby 1.第一次在windows里安装ruby,装的最新版,当时就遇到了很奇怪的编码问题,如运行命令gem install xxx,提示编码错 ...
- 如何实现浏览器的Console功能
离 JS-Encoder 的最初版本发布已经过了大半年的时间,这段时间除了偶尔修复一下 BUG 外,主要还是忙于学业.最近一段时间不太平,开学时间也大大延迟,加上自己本身对自己的在线编译器不是很满意, ...
- Js,JQuery不同方式绑定的同一事件可以同时触发,互不干扰
比如,onclick绑定,然后jquery.on("click", function(){})绑定等