几个核心对象:

  1. ApplicationBuilder 就是startup->Configure方法的第一个参数,请求(HttpContext) 就是由这个类来处理的
  2. HttpContext 这个就不解释了
  3. RequestDelegate 一个异步委托,委托的参数就是HttpContext,定义了一些对HttpContext的操作;可以看做是一个Action<HttpContext>(),只不过方法体内必须有异步的代码(await )

下面解释下ApplicationBuilder,这个类内部维护了一个中间件的列表,还有几个核心的方法:

  1. Use(Func<RequestDelegate, RequestDelegate> middleware),没错,就是我们常用的那个app.User(...)方法。作用是把中间件加到中间件列表
  2. Build(),构建此应用程序用于处理HTTP请求的委托。就是把HttpContext传递给中间件,由中间件处理完成后将结果返回给用户

再看看网上经典的管道图:

请求过来后,

  1. 执行中间件1的逻辑
  2. 调用Next()把处理后的HttpContext传递给中间件2
  3. 执行中间件2内的逻辑
  4. 调用Next()把HttpContext传递给中间件3
  5. 执行中间件3的逻辑
  6. 因为中间件3内没有next(),所以请求流转回中间件2
  7. 执行中间件2中next()方法后面定义的逻辑,请求流转回中间件1
  8. 执行中间件1中next()方法后的逻辑,返回结果Response。

下面是模拟的代码,因为使用了很多委托,比较烧脑,所以加了N多注释,不知道能不能说的清楚

 using System;
using System.Collections.Generic;
using System.Threading.Tasks; namespace RequestPipe
{
class Program
{
static void Main(string[] args)
{
// 实例化一个处理程序
var builder = new ApplicationBuilder(); // 把中间件加入处理程序,给HttpContext的Name加点内容
// 中间件一定要调用Next(),不然不会向后传递
// await 不解释了,当代码执行到这句后,程序会进入next()的执行流程而不会继续执行后面的语句
// 也就是说会显示 第一个开始执行,但 第一个执行结束 这句会等到 next() 运行完成后才执行
builder.Use((next) =>
async (context) =>
{
Console.WriteLine("**********第一个开始执行**********");
context.Name += "First;";
await next(context);
Console.WriteLine("**********第一个结束执行**********");
}
); builder.Use((next) =>
async (context) =>
{
Console.WriteLine("**********第二个开始执行**********");
context.Name += "Second;"; // 执行委托的方法的标准写法,也可以直接next(context)
await next.Invoke(context);
Console.WriteLine("**********第二个结束执行**********");
}
); // 特地用匿名函数来写一个,希望看起来稍微清晰一点
builder.Use(
new Func<RequestDelegate, RequestDelegate>(
delegate (RequestDelegate next)
{
return new RequestDelegate(async delegate (HttpContext context)
{
Console.WriteLine("**********第三个开始执行**********");
context.Name += "Third;";
await next(context);
Console.WriteLine("**********第三个开始执行**********");
});
}
)
); // 执行处理
builder.Build(); Console.ReadLine();
}
} public class ApplicationBuilder
{
// 中间件列表
private List<Func<RequestDelegate, RequestDelegate>> middlewares = new List<Func<RequestDelegate, RequestDelegate>>(); public void New()
{ } public void Build()
{
// 先构建一个基础的HttpContext
var baseContext = new HttpContext(); // 构建一个默认的委托(HttpContext的处理方法),就叫中间件0吧
// 如果没有经过中间件处理,就直接输出404
// 如果中间件处理成功,这里应该是输出 First;Second;Third;
var baseDelegate = new RequestDelegate(async (context) =>
{
context.Name = string.IsNullOrWhiteSpace(context.Name) ? "" : context.Name;
await context.Show();
}); // 把中间件列表的顺序反转一下
middlewares.Reverse(); // 遍历中间件列表
foreach (var middleware in middlewares)
{
// 还记得moddleware的类型吧,传入一个RequestDelegate,返回一个RequestDelegate // 经过上面的反转,现在第一个元素应该是中间件3
// baseDelegate也就是中间件0现在作为参数传递给中间件3
// 中间件3内部通过 await next(context); 保存了对默认委托的调用
// 然后将中间件3返回
// 现在 baseDelegate = 中间件3
// 接下来进入列表的第二个元素,也就是中间件2
// 和上面的逻辑一样,中间件2保存了对中间件3的引用,然后将中间件2返回出来
// ...
// 列表遍历完成后,baseDelegate = 中间件1
baseDelegate = middleware.Invoke(baseDelegate);
} // 执行中间件1
// 中间件1中保存了对中间件2的引用,所以运行到await next()的时候,就会进入到中间件2
// 同理中间件2会进入到中间件3,中间件3进入默认委托,也就是中间件0
// 中间件0执行完成(此程序中就是打印HttpContext的Name属性)返回中间件3
// 然后依次返回到中间件1,最终结束执行
baseDelegate.Invoke(baseContext);
} public void Use(Func<RequestDelegate, RequestDelegate> middleware)
{
middlewares.Add(middleware);
}
} public class HttpContext
{
public string Name { get; set; } public async Task Show()
{
Console.WriteLine(Name);
await Task.CompletedTask;
}
} public delegate Task RequestDelegate(HttpContext context); }

执行结果

**********第一个开始执行**********
**********第二个开始执行**********
**********第三个开始执行**********
First;Second;Third;
**********第三个开始执行**********
**********第二个结束执行**********
**********第一个结束执行**********

OK,这里的难点就是委托套委托,讲真的委托这东西确实强大,但代码读起来真的很难受,后面还是要整理下关于委托使用的文档,加深理解才行。

【aspnetcore】模拟中间件处理请求的管道的更多相关文章

  1. .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法

    .NET Core中间件的注册和管道的构建(3) ---- 使用Map/MapWhen扩展方法 0x00 为什么需要Map(MapWhen)扩展 如果业务逻辑比较简单的话,一条主管道就够了,确实用不到 ...

  2. .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理

    .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理 0x00 问题的产生 管道是.NET Core中非常关键的一个概念,很多重要的组件都以中间件的形式存在,包括权限管理.会话管理 ...

  3. WebAuthorize(中间件对所有请求进行拦截)core只有通过添加中间件过滤请求方式 而非继承然后写特性的那种方式

    一.WebAuthorize 1.项目名称 WebAuthorize 2.加个中间件 过滤请求. using Microsoft.AspNetCore.Builder; using Microsoft ...

  4. .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类

    .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类 0x00 为什么要引入扩展方法 有的中间件功能比较简单,有的则比较复杂,并且依赖其它组件.除 ...

  5. PHP模拟发送POST请求之五curl基本使用和多线程优化

    今天来介绍PHP模拟发送POST请求的重型武器——cURL函数库的使用和其多线程的优化方法. 说起cURL函数,可谓是老生常谈,但网上许多资料都在关键部分语焉不详,列出一大堆手册上的东西,搞得我入门时 ...

  6. PHP模拟发送POST请求之一、HTTP协议头部解析

    WEB开发中信息基本全是在POST与GET请求与响应中进行,GET因其基于URL的直观,易被我们了解,可POST请求因其信息的隐蔽,在安全的同时,也给开发者们模拟发送带来了麻烦.接下来的几篇博文中,我 ...

  7. Loadrunner模拟JSON接口请求进行测试

    Loadrunner模拟JSON接口请求进行测试     一.loadrunner脚本创建 1.Insert - New step -选择Custom Request -  web_custom_re ...

  8. WebClient模拟发送Post请求

    WebClient模拟发送Post请求方法: /// <summary> /// 模拟post请求 /// </summary> /// <param name=&quo ...

  9. httpClient模拟浏览器发请求

    一.介绍 httpClient是Apache公司的一个子项目, 用来提高高效的.最新的.功能丰富的支持http协议的客户端编程工具包.完成可以模拟浏览器发起请求行为. 二.简单使用例子 : 模拟浏览器 ...

随机推荐

  1. postNotificationName 消息传递详解

      1.定义消息创建的关联值 也就是找到方法的标志 NSString *const GameToIPhoneNotification = @"GameToIPhoneNotification ...

  2. html5--5-6 绘制圆/弧

    html5--5-6 绘制圆/弧 学习要点 掌握arc() 方法创建圆弧/曲线(用于创建圆或部分圆) 矩形的绘制方法 rect(x,y,w,h)创建一个矩形 strokeRect(x,y,w,hx,y ...

  3. Git学习笔记 - Git安装

    Git安装(Windows) 从 https://git-for-windows.github.io/ 下载Git,下载完成,双击安装,一路选择默认设置即可. 注意:选择使用git的命令行模式,选择默 ...

  4. netty ssl

    netty提供的例子中有secury的实现,不过是一个伪证书.修改了一下其中的SecureChatSslContextFactory类,使用证书的方式实现ssl.修改后代码如下: public fin ...

  5. 001-将Python源码转换为不需要环境的可执行文件

    1 安装pyinstaller pip install pyinstaller #或者 pip3 install pyinstaller 2 生成打包文件 在命令行中输入 pyinstaller -F ...

  6. 使用 WinSCP(下载) 上文件到 Linux图文教程

        问题导读: 1.如何远程链接? 2.如何上传文件? 3.如何对立面的文件进行操作? 4.什么情况下会链接失败? https://yunpan.cn/cYWtNMycjeVPv 访问密码 4f7 ...

  7. Jmeter 在什么情况下定义多个thread group?

    Jmeter里面有三种线程组:setUp thread group, TearDown thread group, thread group. 如果想定义100个用户登录系统,60个用户做A操作,40 ...

  8. windows下的套接字IO模型

    一般情况下,IO操作的行为受两种因素的影响: IO操作对象的类型(阻塞还是非阻塞) 获取IO操作结果的方式(同步还是异步). 同步就是指操作的发起和操作结果的获取由调用者完成. 异步指操作发起由调用方 ...

  9. mysql:mysql安装及一些配置和图形界面介绍

    关于mysql,我使用的不多,这次记录是在学习一位大牛的demo的契机下完成的.包括前面记录到的sqlserver的一些知识点,也是在新的项目组中协助时候学习记录的.希望通过记录加深印象和帮助后来者. ...

  10. Java的栈和堆

    JVM的内存区域可以被分为:线程栈,堆,静态方法区(实际上还有更多功能的区域,并且这里说的是JVM的内存区域) 线程栈:      注意这个栈和数据结构中的stack有相似之处,但并不是用户态的.准确 ...