【aspnetcore】模拟中间件处理请求的管道
2024-08-30 09:58:29
几个核心对象:
- ApplicationBuilder 就是startup->Configure方法的第一个参数,请求(HttpContext) 就是由这个类来处理的
- HttpContext 这个就不解释了
- RequestDelegate 一个异步委托,委托的参数就是HttpContext,定义了一些对HttpContext的操作;可以看做是一个Action<HttpContext>(),只不过方法体内必须有异步的代码(await )
下面解释下ApplicationBuilder,这个类内部维护了一个中间件的列表,还有几个核心的方法:
- Use(Func<RequestDelegate, RequestDelegate> middleware),没错,就是我们常用的那个app.User(...)方法。作用是把中间件加到中间件列表
- Build(),构建此应用程序用于处理HTTP请求的委托。就是把HttpContext传递给中间件,由中间件处理完成后将结果返回给用户
再看看网上经典的管道图:
请求过来后,
- 执行中间件1的逻辑
- 调用Next()把处理后的HttpContext传递给中间件2
- 执行中间件2内的逻辑
- 调用Next()把HttpContext传递给中间件3
- 执行中间件3的逻辑
- 因为中间件3内没有next(),所以请求流转回中间件2
- 执行中间件2中next()方法后面定义的逻辑,请求流转回中间件1
- 执行中间件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,这里的难点就是委托套委托,讲真的委托这东西确实强大,但代码读起来真的很难受,后面还是要整理下关于委托使用的文档,加深理解才行。
最新文章
- 用scikit-learn学习主成分分析(PCA)
- InfluxDB学习之InfluxDB的HTTP API写入操作
- [转载]Masonry介绍与使用实践(快速上手Autolayout)
- 二、JavaScript语言--JS基础--JavaScript进阶篇--JavaScript内置对象
- Nginx 利用 X-Accel-Redirect response.setHeader 控制文件下载
- CHECKBOX_CHECKED built-in in Oracle D2k Forms
- 【linux】如何查看和解压缩rpm文件内容
- OpenLDAP 安装及配置 笔记
- emWin显示文本字符-【worldsing笔记】
- 关于android的屏幕保持常亮
- 基于shiro授权过程
- Java类加载的时机
- Mycat 分片规则详解--ER关系表分片
- Java三大特性
- [20180810]exadata--豆腐渣系统的保护神.txt
- MyEclipse10 复制之前的项目部署到tomcat时项目名称对不上,还是复制前的项目名称,哪里修改设置
- NN:实现BP神经网络的回归拟合,基于近红外光谱的汽油辛烷值含量预测结果对比—Jason niu
- 获取真实ip三个方法
- (4.24)sql server变量中set与select的区别
- WCF入门教程(二)从零做起-创建WCF服务