让 .NET 轻松构建中间件模式代码(二)--- 支持管道的中断和分支

Intro

上次实现了一个基本的构建中间件模式的中间件构建器,现在来丰富一下功能,让它支持中断和分支,分别对应 asp.net core 中的 applicationBuilder.RunapplicationBuilder.MapWhen

实现管道中断

实现中间件的中断其实很简单,通过上一次的分析我们已经知道,中间件每一个部分其实是一个上下文和 next 的委托,只需要忽略 next,不执行 next 就可以了,就可以中断后面中间件的执行。

定义一个 Run 扩展方法来实现方便的实现中间件中断:

  1. public static IPipelineBuilder<TContext> Run<TContext>(this IPipelineBuilder<TContext> builder, Action<TContext> handler)
  2. {
  3. return builder.Use(_ => handler);
  4. }
  5. public static IAsyncPipelineBuilder<TContext> Run<TContext>(this IAsyncPipelineBuilder<TContext> builder, Func<TContext, Task> handler)
  6. {
  7. return builder.Use(_ => handler);
  8. }

实现分支

分支的实现主要是参考 asp.net core 里 applicationBuilder.Map/applicationBuilder.MapWhen 实现分支路由的做法,在 asp.net core 里,MapWhen 是一个扩展方法,其实现是一个 MapWhenMiddleware,有兴趣可以看 asp.net core 的源码。

实现原理也挺简单的,其实就是满足分支的条件时创建一个全新的中间件管道,当满足条件的时候就就执行这个分支中间件管道,否则就跳过这个分支进入下一个中间件。

首先在 PipelineBuilder 的接口定义中增加了一个 New 方法用来创建一个全新的中间件管道,定义如下:

  1. public interface IPipelineBuilder<TContext>
  2. {
  3. IPipelineBuilder<TContext> Use(Func<Action<TContext>, Action<TContext>> middleware);
  4. Action<TContext> Build();
  5. IPipelineBuilder<TContext> New();
  6. }
  7. //
  8. public interface IAsyncPipelineBuilder<TContext>
  9. {
  10. IAsyncPipelineBuilder<TContext> Use(Func<Func<TContext, Task>, Func<TContext, Task>> middleware);
  11. Func<TContext, Task> Build();
  12. IAsyncPipelineBuilder<TContext> New();
  13. }

实现就是直接创建了一个新的 PipelineBuilder<TContext> 对象,示例如下:

  1. internal class PipelineBuilder<TContext> : IPipelineBuilder<TContext>
  2. {
  3. private readonly Action<TContext> _completeFunc;
  4. private readonly List<Func<Action<TContext>, Action<TContext>>> _pipelines = new List<Func<Action<TContext>, Action<TContext>>>();
  5. public PipelineBuilder(Action<TContext> completeFunc)
  6. {
  7. _completeFunc = completeFunc;
  8. }
  9. public IPipelineBuilder<TContext> Use(Func<Action<TContext>, Action<TContext>> middleware)
  10. {
  11. _pipelines.Add(middleware);
  12. return this;
  13. }
  14. public Action<TContext> Build()
  15. {
  16. var request = _completeFunc;
  17. for (var i = _pipelines.Count - 1; i >= 0; i--)
  18. {
  19. var pipeline = _pipelines[i];
  20. request = pipeline(request);
  21. }
  22. return request;
  23. }
  24. public IPipelineBuilder<TContext> New() => new PipelineBuilder<TContext>(_completeFunc);
  25. }

异步的和同步类似,这里就不再赘述,有疑问可以直接看文末的源码链接

接着就可以定义我们的分支扩展了

  1. public static IPipelineBuilder<TContext> When<TContext>(this IPipelineBuilder<TContext> builder, Func<TContext, bool> predict, Action<IPipelineBuilder<TContext>> configureAction)
  2. {
  3. return builder.Use((context, next) =>
  4. {
  5. if (predict.Invoke(context))
  6. {
  7. var branchPipelineBuilder = builder.New();
  8. configureAction(branchPipelineBuilder);
  9. var branchPipeline = branchPipelineBuilder.Build();
  10. branchPipeline.Invoke(context);
  11. }
  12. else
  13. {
  14. next();
  15. }
  16. });
  17. }

使用示例

我们可以使用分支和中断来改造一下昨天的示例,改造完的示例如下:

  1. var requestContext = new RequestContext()
  2. {
  3. RequesterName = "Kangkang",
  4. Hour = 12,
  5. };
  6. var builder = PipelineBuilder.Create<RequestContext>(context =>
  7. {
  8. Console.WriteLine($"{context.RequesterName} {context.Hour}h apply failed");
  9. })
  10. .When(context => context.Hour <= 2, pipeline =>
  11. {
  12. pipeline.Use((context, next) =>
  13. {
  14. Console.WriteLine("This should be invoked");
  15. next();
  16. });
  17. pipeline.Run(context => Console.WriteLine("pass 1"));
  18. pipeline.Use((context, next) =>
  19. {
  20. Console.WriteLine("This should not be invoked");
  21. next();
  22. Console.WriteLine("will this invoke?");
  23. });
  24. })
  25. .When(context => context.Hour <= 4, pipeline =>
  26. {
  27. pipeline.Run(context => Console.WriteLine("pass 2"));
  28. })
  29. .When(context => context.Hour <= 6, pipeline =>
  30. {
  31. pipeline.Run(context => Console.WriteLine("pass 3"));
  32. })
  33. ;
  34. var requestPipeline = builder.Build();
  35. Console.WriteLine();
  36. foreach (var i in Enumerable.Range(1, 8))
  37. {
  38. Console.WriteLine($"--------- h:{i} apply Pipeline------------------");
  39. requestContext.Hour = i;
  40. requestPipeline.Invoke(requestContext);
  41. Console.WriteLine("----------------------------");
  42. }

输出结果如下:

看输出结果我们可以看到 Run 后面注册的中间件是不会执行的,Run 前面注册的中间件正常执行

然后定义的 When 分支也是正确执行的~~

Reference

让 .NET 轻松构建中间件模式代码(二)的更多相关文章

  1. 让 .NET 轻松构建中间件模式代码

    让 .NET 轻松构建中间件模式代码 Intro 在 asp.net core 中中间件的设计令人叹为观止,如此高大上的设计何不集成到自己的代码里呢. 于是就有了封装了一个简单通用的中间件模板的想法, ...

  2. Java工作流引擎-中间件模式代码集成

    关键词:工作流快速开发平台  工作流流设计  业务流程管理   asp.net 开源工作流  bpm工作流系统  java工作流主流框架  自定义工作流引擎 表单设计器  流程设计器 前端代码集成步骤 ...

  3. ASP.NET Core 1.0中的管道-中间件模式

    ASP.NET Core 1.0借鉴了Katana项目的管道设计(Pipeline).日志记录.用户认证.MVC等模块都以中间件(Middleware)的方式注册在管道中.显而易见这样的设计非常松耦合 ...

  4. Core 1.0中的管道-中间件模式

    ASP.NET Core 1.0中的管道-中间件模式 SP.NET Core 1.0借鉴了Katana项目的管道设计(Pipeline).日志记录.用户认证.MVC等模块都以中间件(Middlewar ...

  5. Java设计模式之《构建者模式》及应用场景

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6553374.html 构建者模式,又称建造者模式,将一部负责对象的构建分为许多小对象的构建 ...

  6. 深入浅出的webpack构建工具---DevServer配置项(二)

    深入浅出的webpack构建工具---DevServer配置项(二) 阅读目录 DevServer配置项 1. contentBase 2. port 3. host 4. headers 5. hi ...

  7. 深入探索Java设计模式之构建器模式(五)

    抽丝剥茧 细说架构那些事——[优锐课] 简单的程序不需要大量的设计过程,因为它们只关注有限的解决方案,仅使用几个类.大型程序专注于广泛的设计,该设计比好的设计范例的任何其他属性都更能利用可重用性.宏伟 ...

  8. 前端构建大法 Gulp 系列 (二):为什么选择gulp

    系列目录 前端构建大法 Gulp 系列 (一):为什么需要前端构建 前端构建大法 Gulp 系列 (二):为什么选择gulp 前端构建大法 Gulp 系列 (三):gulp的4个API 让你成为gul ...

  9. WCF学习之旅—TCP双工模式(二十一)

    WCF学习之旅—请求与答复模式和单向模式(十九) WCF学习之旅—HTTP双工模式(二十) 五.TCP双工模式 上一篇文章中我们学习了HTTP的双工模式,我们今天就学习一下TCP的双工模式. 在一个基 ...

随机推荐

  1. .tar.xz文件的创建和解压

    创建tar.xz文件:只要先 tar cvf xxx.tar xxx/ 这样创建xxx.tar文件先,然后使用 xz -z xxx.tar 来将 xxx.tar压缩成为 xxx.tar.xz 解压ta ...

  2. redis相关命令及应用场景

    Redis的应用场景 (1)         配合关系型数据库做高速缓存 l  高频次,热门访问的数据,降低数据库IO l  高频次,热门访问的数据,降低数据库IO (2)         由于其拥有 ...

  3. python的进制转换

    转载于:https://www.cnblogs.com/FWF1944/p/11132409.html(方法论190404) Python整数能够以十六进制,八进制和二进制来编写,作为一般以10位基数 ...

  4. JavaScript(9)--- 跨域

    JavaScript(9)--- 跨域 一.跨域原理(同源策略) 在项目搭建的初期,因为现在项目基本上都是前后端分离,所以不可避免地会遇到跨域问题,而造成跨域的罪魁祸首就是浏览器的同源策略.所以要解决 ...

  5. 【Unity游戏开发】跟着马三一起魔改LitJson

    一.引子 在游戏开发中,我们少不了和数据打交道,数据的存储格式可谓是百花齐放,xml.json.csv.bin等等应有尽有.在这其中Json以其小巧轻便.可读性强.兼容性好等优点受到广大程序员的喜爱. ...

  6. STL vector容器 和deque容器

    前言 STL是C++的框架,然后vector容器和deque容器又是STL的一部分... 这块的内容都是理解.概念为主,没什么捷径,希望读者能静下来记. 先来讲vector容器(单端动态数组) 1.v ...

  7. MySQL到底能有多少个字段

    今天技术讨论群里 “一切随遇而安”同学看书时出现一个疑问,一个MySQL的表中到底可以有多少个字段?带着这个疑问,我们展开了探讨,也接着讨论了一个单字段长度的问题. 1.  官方文档说明 官方文档的内 ...

  8. 5分钟配置好你的AI开发环境

    作者 | Revolver 无论是第一次设置TensorFlow的新手数据科学爱好者,还是使用TB级数据的经验丰富的AI工程师,安装库.软件包或者框架总是一个困难又繁琐的过程.但是像Docker这样的 ...

  9. [React]Hook初探

    Hook是什么 Hook是React从16.8开始支持的特性,使用Hook可以在不使用class时使用state Hook支持在不需要修改组件状态的情况下复用逻辑状态,从而解决使用render pro ...

  10. 模块 collections 高级数据类型

    collections模块 原文来自cnblog 的 Eva-J Eva-J 介绍了collections模块的常用方法,和演示实例 在 Python cookbook 的第一章中还有一些 更加好玩的 ...