在.Net Core的源码中,很多地方都有中间件的地方,Kestrel Server和Asp.net Core 等都用了中间件的设计,比如在Kestrel Server中,Http协议的1.0, 1.1, 2.0分别注册了不同的中间件从而导致不同方式的解析报文,这些要求了我们如何设计一个优雅的中间件框架,在MSDN 上这样描述了asp.net core的 中间件,每个中间件都应该

  • Chooses whether to pass the request to the next component in the pipeline.(选择是否将请求传递到管道中的下一个组件)
  • Can perform work before and after the next component in the pipeline.(可在管道中的下一个组件前后执行工作)

这无疑给了中间件的设计难度,在经典模型里,asp.net还是停留在管道模型里,定义了十几个event,分别执行在不同的时间节点,然后在不同的module里会注册自己的行为到事件上,在当时的观念来看,这是一个非常好的代码设计,面向切面编程,不同的module分工明细降低耦合性,但是随之而来带来的是臃肿全家桶设计,在当前的微服务年代,我们需要动态的添加这些设计,比如认证授权session module。

而现在的asp.net core 的中间件设计非常的好,可以拿到下一个中间件的控制权,并且在下一个中间件之前或者结束做其他的工作。如果不熟悉中间件的同学可以看看 msdn 的描述,这里我们来根据源码自己实现一个优雅的中间件。

首先我们要达成的效果是这样的

public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
// Do work that doesn't write to the Response.
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
}); app.Run(async context =>
{
await context.Response.WriteAsync("Hello from 2nd delegate.");
});
}
}

中间件的执行顺序是这样的

从上面的用法可以看到,我们需要定义一个IApplicationBuilder,然后用use去注册中间件到applicationbuild 对象,所以我们定义一个IApplicationBuilder

 public interface IApplicationBuilder {

        IApplicationBuilder Use(Func<HttpContext, Func<Task>, Task> middleware);//注册中间件

        IApplicationBuilder Build();//生成委托链

        IApplicationBuilder Run();//调用委托链

    }

Use的接口设计,是为了我们上面实现的效果,传入一个方法指针(委托,后略),这个指针需要两个参数,一个HttpContext,一个是下一个管道的方法指针,返回一个task对象, 现在为了让我们的代码跑起来,再定义一个HttpContext对象如下。

 public class HttpContext {

    }

现在让我们去实现一个这个接口

   public delegate Task RequestDelegate(HttpContext httpContext);

  class DefaultApplicationBuilder : IApplicationBuilder { public static List<Func<RequestDelegate, RequestDelegate>> _components = new ();
public IApplicationBuilder Use(Func<HttpContext,Func<Task>,Task> middleware) { Func<RequestDelegate, RequestDelegate> component = next => {
return context => {
Func<Task> task = () => next(context);
return middleware(context,task);
};
}; _components.Add(component); return this;
}
}

现在我们分析Use的实现,首先我们定义了一个方法指针RequestDelegate,这个没啥说的,而这个的设计妙处在DefaultApplicationBuilder中维护了一个 _components对象,是一个集合对象,定义了“二级”方法指针对象,这里的二级指的是Func<Func<T>>对象,得到一个“一级”方法指针处理后返回另一个“一级”方法指针。现在我们看一下这个Use方法的实现,一个中间件middleware就相当于一个方法指针,这时候它定义了一个component,获取一个方法指针,然后返回一个方法指针,注意在返回的方法指针里,它将之前传入的方法指针重新包装了一下得到task对象,这个相当于二级的指针,然后传给中间件。这个地方有点绕。大家需要多看一下理解其中的含义。

然后我们再实现一下build 和run 方法如下。

 public IApplicationBuilder Build() {

            RequestDelegate app = context => Task.CompletedTask;

            _components.Reverse();

            foreach (var component in _components) {

                app = component(app);
} requestDelegate = app; return this;
} public IApplicationBuilder Run() { var context = new HttpContext(); requestDelegate(context); return this;
}

简单说一下build方法,这里的设计之妙就在于将“二级”指针转发成“一级”指针并生成一个委托链,其中的next参数装的就是一系列的委托链。返回的就是第一个注册的中间件。现在我们使用一下这个中间件吧。

 static void Main(string[] args) {

            IApplicationBuilder applicationBuilder = new DefaultApplicationBuilder();

            applicationBuilder.Use(async (context, next) => {
Console.WriteLine(1);
await next.Invoke();
Console.WriteLine(2); }); applicationBuilder.Use(async (context, next) => {
Console.WriteLine(3);
await next.Invoke();
Console.WriteLine(4);
}); applicationBuilder.Use(async (context, next) => {
Console.WriteLine(5);
await next.Invoke();
Console.WriteLine(6);
}); applicationBuilder
.Build()
.Run(); Console.WriteLine("Hello World!");
}

返回结果就是如下,就是msdn文档所说的调用逻辑。

1
3
5
6
4
2
Hello World!

这一块理解起来比较难,设计了这中间件这一块的人很厉害,已经将代码上传到github 上了,大家有兴趣可以对比代码来研究分析。如果有任何问题欢迎大家留言。谢谢大家的阅读

.Net Core如何优雅的实现中间件的更多相关文章

  1. Asp.Net Core 第06局:中间件

    总目录 前言 本文介绍Asp.Net Core 中间件. 环境 1.Visual Studio 2017 2.Asp.Net Core 2.2 开局 第一手:中间件概述     1.中间件:添加到应用 ...

  2. .Net Core Web Api实践之中间件的使用(一)

    前言:从2019年年中入坑.net core已半年有余,总体上来说虽然感觉坑多,但是用起来还是比较香的.本来我是不怎么喜欢写这类实践分享或填坑记录的博客的,因为初步实践坑多,文章肯定也会有各种错误,跟 ...

  3. ASP.NET Core 中的那些认证中间件及一些重要知识点

    前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...

  4. [转]ASP.NET Core 中的那些认证中间件及一些重要知识点

    本文转自:http://www.qingruanit.net/c_all/article_6645.html 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系 ...

  5. asp.net core 教程(六)-中间件

    Asp.Net Core-中间件 在这一章,我们将了解如何设置中间件.中间件技术在 ASP.NET Core中控制我们的应用程序如何响应 HTTP 请求.它还可以控制应用程序的异常错误,这是一个在如何 ...

  6. Asp .Net core 2 学习笔记(2) —— 中间件

    这个系列的初衷是便于自己总结与回顾,把笔记本上面的东西转移到这里,态度不由得谨慎许多,下面是我参考的资源: ASP.NET Core 中文文档目录 官方文档 记在这里的东西我会不断的完善丰满,对于文章 ...

  7. 将参数传递给ASP.NET Core 2.0中的中间件

    问题 在ASP.NET Core的安装过程中,如何将参数传递给中间件? 解 在一个空的项目中添加一个POCO类来保存中间件的参数, publicclass GreetingOptions { publ ...

  8. 在ASP.NET Core中编写合格的中间件

    这篇文章探讨了让不同的请求去使用不同的中间件,那么我们应该如何配置ASP.NET Core中间件?其实中间件只是在ASP.NET Core中处理Web请求的管道.所有ASP.NET Core应用程序至 ...

  9. Asp.Net Core Endpoint 终结点路由之中间件应用

    一.概述 这篇文章主要分享Endpoint 终结点路由的中间件的应用场景及实践案例,不讲述其工作原理,如果需要了解工作原理的同学, 可以点击查看以下两篇解读文章: Asp.Net Core EndPo ...

随机推荐

  1. 15 自动发布Java项目(Tomcat)

    #!/bin/bash export PAHT=/usr/local/maven/bin:/usr/local/jdk/bin:/usr/local/sbin:/usr/local/bin:/usr/ ...

  2. 最新的.NET 热重载介绍

    今天,我们很高兴的向您介绍 Visual Studio 2019 版本 16.11(预览版 1)和 .NET 6 中的 dotnet watch 命令行工具(预览版 4)中的 .NET 热重载体验的可 ...

  3. Source not found for GeneratedMethodAccessor127.invoke(Object, Object[]) line: not available

    报错:Source not found for GeneratedMethodAccessor127.invoke(Object, Object[]) line: not available 我在使用 ...

  4. buu [MRCTF2020]keyboard

    密文: ooo yyy ii w uuu ee uuuu yyy uuuu y w uuu i i rr w i i rr rrr uuuu rrr uuuu t ii uuuu i w u rrr ...

  5. String、StringBuilder和StringBuffer的比较

    目录 1.String特性 1.1 不可变 1.2 字符串常量池 2.StringBuilder和StringBuffer 2.1 区别 2.2 应用场景 1.String特性 1.1 不可变 它是I ...

  6. java基础---类和对象(4)

    一. static关键字 使用static关键字修饰成员变量表示静态的含义,此时成员变量由对象层级提升为类层级,整个类共享一份静态成员变量,该成员变量随着类的加载准备就绪,与是否创建对象无关 使用st ...

  7. Django基础-003 配置Django自带的后台管理,操作数据库

    插入测试数据,可以自己写页面来插入数据 也可以使用Django自带的后台管理,来操作数据表 1.创建用户 python manage.py createsuperuser 2.在浏览器输入地址,进入D ...

  8. python 常用命令sys.exit()

    Python的程序有两中退出方式:os._exit(), sys.exit() os._exit()会直接将python程序终止,之后的所有代码都不会继续执行. sys.exit()会引发一个异常:S ...

  9. PYTHON 利用ImagePipeline专门爬取图片

    自定义file_path()函数,即可以原有图像文件名为名来保存,并分类保存 def file_path(self, request, response=None, info=None): image ...

  10. DIV+css排版问题技巧总结---v客学院技术分享

                DIV+css排版问题技巧总结 一.排版思路 1.从上到下,从左到右,从大到小. 2.首先确定排版分区,排除色块分布,然后再从简单的部分开始. 3.在某一块内将HTML部分写好 ...