NET6 WebApi第5讲:中间件(源码理解,俄罗斯套娃怎么来的?);Web 服务器 (Nginx / IIS / Kestrel)、WSL、SSL/TSL

一、NET6的启动流程

区别:

.NET6 WebApi第1讲:VSCode开发.NET项目、区别.NET5框架【两个框架启动流程详解】_vscode webapi-CSDN博客

2、WebApplicationBuilder:是NET6引入的一个类,是建造者模式的典型应用

1>建造者模式的核心思想

  • 将一个复杂对象的构建过程分解为多个步骤。

  • 通过一个"建造者"对象逐步配置和构建最终的对象。

2>WebApplicationBuilder代码如下图

先创建一个Buider(得到建造者)------再build生成一下(得到一个Web应用程序)------最后Run得到想要得到的东西

二、管道------服务器处理客户端请求 的各个环节

1、Http请求响应流程

2、Web 服务器 (Nginx / IIS / Kestrel)

**1>Nginx:**高性能 Web 服务器和反向代理,跨平台。

Nginx 以高性能和低资源消耗著称,适用于处理大量并发连接。

1》反向代理(区别正向代理)

1)反向代理:代理的是服务器,保护服务器并提升服务器性能。

一种代理服务器,它位于客户端和服务器之间,代表服务器接收和处理来自客户端的请求,然后将响应返回给客户端。

重点:客户端并不知道真正的后端服务器,只知道反向代理服务器。

2)正向代理:代理的是客户端,帮助客户端访问外部网络或受限资源。

3)两者区别:除了代理对象不同,还有使用目的不同

  • 正向代理 :主要用于访问受限资源、保护客户端隐私和加速访问。
    • 假设你身处一个网络环境受限的地方,无法直接访问某些国外网站(如Google)。这时,你可以使用正向代理服务器来绕过这些限制。
    • 你配置了一个可以访问Google的正向代理服务器。你通过浏览器设置代理服务器的地址和端口,然后再次访问Google。
  • 反向代理 :主要用于负载均衡、安全防护、缓存加速等,提升服务器性能和安全性。
    • 假设你是一家电商网站的管理员,你的网站每天有大量的用户访问。为了提升网站的性能和安全性,你决定使用反向代理服务器。
    • **代理服务器接收请求:**用户的请求首先到达反向代理服务器。反向代理服务器作为网站的入口,接收并处理所有来自用户的请求。
    • **代理服务器转发请求:**反向代理服务器根据配置的策略(如负载均衡、缓存等),将请求转发给内部的真实服务器。

2>IIS:微软提供的 Web 服务器,仅限 Windows。

它提供全面的企业级功能,包括负载均衡、安全性配置、身份验证等。

3>Kestrel: 轻量级 ASP.NET Core Web 服务器,跨平台。

Kestrel 是轻量级且专为 ASP.NET Core 设计的服务器,适合作为反向代理后的应用服务器。

Kestrel与反向代理(如 Nginx)配合使用)

3、【题外话】开发工具:WSL

  • 提供完整的 Linux 内核接口,允许用户在 Windows 上直接运行 Linux 环境。
  • 无需虚拟机,性能接近原生 Linux。
  • 主要用于开发、测试和运行 Linux 应用。

4、【题外话】安全协议(SSL/TSL):TLS 基于 SSL 3.0 开发,是 SSL 的升级版。

SSL(Secure Sockets Layer,安全套接层)是一种用于网络通信的安全协议,主要用于加密和验证数据传输,确保通信的安全性和完整性。SSL证书是实现SSL/TLS(Transport Layer Security)加密的关键,它由受信任的证书颁发机构(CA)签发,用于验证服务器的身份并确保客户端与服务器之间的通信安全

在.NET Core项目中,SSL通常与HTTPS(HTTP Secure)结合使用,用于保护Web应用程序的数据传输安全。

HTTPS通过SSL/TLS协议对数据进行加密,防止数据在传输过程中被窃听、篡改或伪造。

5、什么是管道?------管道配置=请求级的处理

1>为什么 管道配置=请求级的处理 ?

如上图,把所有管道配置都注释了,运行代码。

1)如下图

如果啥也不配置,针对没监听的8005端口,出现"无法访问",代表是没有响应的。

2)如下下图

针对监听的域名+端口7007,出现"找不到",则代表服务器是有响应的,只是响应的是404,没有资源(返回没有任何东西)。

3)由此,证明上图的"管道配置是请求级的处理"。

三、参考二、中"没有管道配置"的情况,我们该如何配置管道?

1、看扩展Extensions

项目代码第4讲:不用Token进行登录(前+后端)、仿照项目代码的逻辑自己实现Token(权限identity、日志Logger)、Swagger如何接收Token?扩展Extensions(静态类)_getwey登录接口不用token-CSDN博客

2、如何配置管道?------都是基于IApplicationBuilder 来配置的

1>管道配置都是基于IApplicationBuilder的,同时有一个Use

如下图,假设配置app.UseHttpsRidirection();

把光标移动到UseHttpsRidirection,按住Fn+F12,航到该扩展方法的定义位置,如下下图。

由下图可知,UseHttpsRidirection配置是基于IApplicationBuilder的,同时有一个Use

2>WebApplication也是继承自IApplicationBuilder,也有一个Use

3、什么是IApplicationBuilder?------(ApplicationBuilder的接口)用于配置请求处理管道的接口

1>IApplicationBuilder 是 ApplicationBuilder的接口。、

2>IApplicationBuilder代码解释(先new,再Use,最后Run(Build)一下)

如下图,可知IApplicationBuilder也是一个建造者模式(见一、)

1》必然会New【是隐式的(框架自动完成),看源代码就知道这里是构造函数!!】:得到一个IApplicationBuilder对象
2》配置需要做的事:利用这个对象配置自己需要做的事,即下图中的Use(...)
3》最后的Build下【隐式的,藏在app.Run()中】
1)阅读app.Run()的源码,如下图

可以看到在app.Run()里面,最后还会执行一次Build

2)区别 配置管道前面的"var app = builder.Build();"。实际例子见六、1、

和上述1)都是执行的同一个ApplicationBuilder里的Build方法,只是作用不同!

《1》var app = builder.Build();作用:

Build 方法返回的 RequestDelegate 只是一个默认的 404 处理逻辑,并没有包含任何中间件。

《2》app.Run()里的作用:

里面实际上会再次调用 Build 方法,以确保所有的中间件都被正确地组合到请求处理管道中。

4、综上,配置管道的方法:基于IApplicationBuilder,

先new()【隐式的】,

再Use,

最后Run(Build)一下【隐式的,藏在app.Run()中。注意区别 配置管道前面的"var app = builder.Build();"】

四、配置管道举例

1、管道配置的源码

通过WebApplication,如下下图蓝色框

展开上图最后一条方法,如下图。这个方法的作用是"Adds the middleware to the application request pipeline."(将中间件添加到应用程序请求管道。)

Func的第一个RequestDelegate是返回类型,第二个RequestDelegate是接收的参数类型。代表"接受一个RequestDelegate并返回一个新的RequestDelegate"

注意:RequestDelegate需要接受一个参数,如下图是RequestDelegate的源代码

2、单个管道配置

1>写出一个管道配置:也是基于IApplicationBuilder,先new(),再Use,最后Run(Build)一下

1》解释上图的next和context
1)上图中的nextcontext 都是传入的参数,但它们的来源和作用不同。

Func<RequestDelegate, RequestDelegate> 是一个函数委托,它接收一个 RequestDelegate(即 next)作为输入,并返回一个新的 RequestDelegate

  • next 是输入参数,代表管道中的下一个中间件(或默认的 404 处理逻辑)。

  • 返回值是一个新的 RequestDelegate,它是一个接收 HttpContext(即 context)并返回 Task 的委托。

2)每个中间件的 context 参数都是同一个 HttpContext 对象

《1》context 是什么?------ HttpContext 对象,它代表当前 HTTP 请求的上下文

  • 它包含了请求的所有信息(如请求头、请求体、路径、查询参数等)和响应的相关信息(如状态码、响应头、响应体等)。

**《2》 context 是什么时候创建的?------**由 ASP.NET Core 框架在请求到达时创建的

  • 具体来说,当 HTTP 请求到达服务器时,ASP.NET Core 的服务器层(如 Kestrel)会解析请求,并创建一个 HttpContext 对象。

  • 这个 HttpContext 对象会被传递给中间件管道,作为管道的入口参数。

《3》 为什么每个中间件的 context 是同一个对象?

  • HttpContext 是请求的上下文对象,它在请求的整个生命周期中保持不变。

  • 中间件管道是一个链式结构,每个中间件都会接收同一个 HttpContext 对象,并在其基础上进行操作。

  • 所有中间件可以共享和修改请求和响应的状态。

  • 例如:

    • Middleware1 可以修改 context.Response 的状态码。

    • Middleware2 可以读取 context.Request 的请求头。

    • Middleware3 可以向 context.Response 写入响应体。

《4》context 是如何传递给中间件的?【 app是什么?详细见3、】

  • Build 方法中,app 是一个 RequestDelegate,它接收 HttpContext 作为参数。

  • 当请求到达时,ASP.NET Core 框架会调用 app,并将 HttpContext 作为参数传递给它。

  • 每个中间件的 Invoke 方法都会接收这个 HttpContext 对象,并将其传递给下一个中间件(通过 await next(context))。

2>开始简化单个管道配置的代码

简化上图,省略new()。如下图

再简化上图,可以去掉大括号{}和 return(下图红框中的)【因为是直接返回一个context,后面没有其它的代码。去掉的原理 在下述链接的 Lambda 委托 部分】C#第6讲:集合ArrayList、List;字典Dictionary;foreach遍历;Func<,>函数(委托/Lambda )_c# dictionary foreach-CSDN博客

再简化上图,变成异步版本。即加上async和await,把return Task.Run()去掉。如下图

最后加上Use部分,运行代码。(只加了这一个中间件)

3>运行代码,显示结果

如下图,无论在哪个路由,只要域名和端口正确了,就会显示(上图)管道中的信息。

再次证明二、中的"管道配置是请求级的处理"。

3、多个管道配置

由上图可知,把"app.Use(里面的变量)"替换成上面的"变量="的东西,如下图

在各个管道间加入"await next.Invoke(context)",如下图,右边是运行结果的截图

配置管道的过程(代码从上到下)=下列的每个圈(从外画到内),管道执行过程=下图的箭头

五、管道执行过程 如何做到 套娃?------【前提】Use、Build方法的源码解读

1、看aspnetcore 的源码:注意核心源码在src文件中

搜出来的第一个就是

如下图,选择需要下载的版本

2、查看中间件的源码,先看Use方法

为了要看清楚Use里面为什么是这个变化,就要去找"app"

如下图,app就是WebApplication

打开1>中下载的aspnetcore源码,核心源码在src中

如下图,直接在src文件中搜索"Application"

打开了源码,如下图

1>在WebApplication源码中查找Use方法

为什么找Use方法?------如下图

找到源码如下图,发现源码调用到ApplicationBuilder 的 Use方法

2>在ApplicationBuilder源码中查找Use方法

aspnetcore源码的src文件中 查找 ApplicationBuilder的源码

3>总结上述过程:按照代码Use的先后顺序,把中间件添加到_components集合中。

举例:

假设有三个中间件,分别是 Middleware1、Middleware2 和 Middleware3。它们的添加顺序如下:

cs 复制代码
app.Use(Middleware1);
app.Use(Middleware2);
app.Run(Middleware3);

那么_components 列表中的顺序是 [Middleware1, Middleware2, Middleware3]

3、查看中间件的源码,再看Build方法

在ApplicationBuilder的源码中找到Build方法,如下图

1>上图中上面的蓝框部分:定义了一个响应404的委托("app"是方法,可以用Invoke调用)

1》具体作用

  • 如果请求到达了管道的末尾(即没有中间件处理请求),它会检查是否有未执行的终结点(endpoint)。如果有,抛出异常;否则,返回 404 状态码。

2》context参数是什么?

  • 是 HttpContext 对象,它代表当前 HTTP 请求的上下文。这个 context 是由 ASP.NET Core 框架在请求到达时创建并传递给中间件管道的。

3》下图中的message部分:"$"是插值表达式

  • 如果请求到达了管道的末尾而没有执行终点:'{endpoint.DisplayName}'。如果您正在使用路由,请使用'{IApplicationBuilder}.UseEndpoints(...)'注册终点中间件。

2>上上图中下面的蓝框部分:添加一个委托集合的循环

1》app = _components[c](app); 的含义
  • _components[c] 是当前中间件的配置函数,就是2、中的_components参数,如下图
  • app 是当前管道的入口(即下一个中间件)。

  • _components[c](app) 调用当前中间件的配置函数,并返回一个新的 RequestDelegate,表示当前中间件处理后的管道。

  • 通过 app = _components[c](app);,将当前中间件"包裹"到管道中。

2》举例

下图中,假设_components.Count=3,那么按照for循环,先取的是下图右边位置为"2"的第三个委托。

下图代码其实是省略了Invoke,完整的这句话应该是:

app = _components[c].Invoke(app);

六、管道执行过程 如何做到 套娃?------具体分析过程

1 、整体分析Program.cs里面所有代码的运行过程

先要看五、,知道Use和Build方法的源码作用。

把Program.cs里的代码完整显示出来(如下图),开始按照代码运行顺序分析。

1>第一次执行Buid方法:var app= builder.Build();

Build方法的源码如下图。

  • **不执行下图的红框部分:**此时因为没有调用过 Use 方法添加任何中间件,所以_components 列表是空的。
  • **只执行下图的蓝框部分:**因此,Build 方法返回的 RequestDelegate 只是一个简单的委托,即默认的 404 处理逻辑。

2>每次调用 Use 方法时,都会将一个新的中间件按顺序添加到 _components 列表中

在 app.UseRouting() 和 app.UseEndpoints(...) 调用后,_components 列表中包含了路由和端点中间件。

3>app.Run();的源码中包括再次调用Build方法 【见三、3、2>3》】

当 app.Run() 启动应用程序时,Build 方法会被再次调用,此时 _components 列表中已经包含了所有的中间件,因此请求处理管道会被正确地构建。

2、结合项目代码分析(具体分析上述3>,再次调用Build方法)

配置三个中间件,如下图

上图的next和context分别代表什么?------见四、2、1>1》2),这里需要的是其中的《4》,如下

《4》 context 是如何传递给中间件的?

  • Build 方法中,app 是一个 RequestDelegate,它接收 HttpContext 作为参数。

  • 当请求到达时,ASP.NET Core 框架会调用 app,并将 HttpContext 作为参数传递给它。

  • 每个中间件的 Invoke 方法都会接收这个 HttpContext 对象,并将其传递给下一个中间件(通过 await next(context))。

1>初始化:默认的 404 处理逻辑

ASP.NET Core 框架在请求到达时会创建一个context(HttpContext 对象),它代表当前 HTTP 请求的上下文)。

所以会有一个默认的 404 处理逻辑。用于处理请求到达管道末尾但没有匹配到任何端点的情况(返回 404 状态码)。

2>按照上图中下面红框的for循环,先取出第三个中间件Middleware3

下图中,next就是传入的参数(上一次循环的结果,此处是初始化的默认 404 处理逻辑),

另一个传入的参数context: ASP.NET Core 框架在请求到达时自动创建的,所有中间件用的context是同一个 HttpContext 对象。

第一次循环(c = 2Middleware3

cs 复制代码
app = Middleware3.Invoke(app);
  • 在第一次 for 循环时,app (后面的那个)是初始化的默认 404 处理逻辑。

  • Middleware3.Invoke(app) 会将当前的 app(即默认的 404 处理逻辑)作为 next 参数传递给 Middleware3

  • Middleware3 返回一个新的 RequestDelegate,这个委托会执行 Middleware3 的逻辑,但不会调用 next(即默认的 404 处理逻辑)。

2》第二次循环(c = 1Middleware2
cs 复制代码
app = Middleware2.Invoke(app);
  • app(后面的那个)现在是 Middleware3 返回的 RequestDelegate

  • app(后面的那个) 被传递给 Middleware2Invoke 方法。

  • Middleware2 返回一个新的 RequestDelegate,这个委托会先执行 Middleware2 的前逻辑,然后调用 app(即 Middleware3 的逻辑),最后执行 Middleware2 的后逻辑。

3》第三次循环(c = 0Middleware1
cs 复制代码
app = Middleware1.Invoke(app);
  • app (后面的那个)现在是 Middleware2 返回的 RequestDelegate

  • app (后面的那个)被传递给 Middleware1Invoke 方法。

  • Middleware1 返回一个新的 RequestDelegate,这个委托会先执行 Middleware1 的前逻辑,然后调用 app(即 Middleware2 的逻辑),最后执行 Middleware1 的后逻辑。

4>综上,这就是俄罗斯套娃的由来

1》 如下图,第一次循环是最里面那个圈(对应第三个中间件)。

嵌套的委托,体现在里面的圈 被 外面的圈 包围。

Http管道请求从下图的左到右,每个中间件的 逻辑 体现在 请求开始(下图蓝线前)接触到的线;逻辑 体现在 请求在(下图蓝线后)接触到的线

2》对应下图,就是管道先添加进去的第一次循环(第三个中间件),是最里面绿色的那圈。

区别代码顺序:配置管道的过程(代码从上到下)=下列的每个圈(从外画到内),管道执行过程=下图的箭头

相关推荐
arron88991 小时前
高性能C#定时删除图片,包含定时触发、分批删除、异步处理和资源监控
c#
月巴月巴白勺合鸟月半2 小时前
工作记录 2017-03-07
c#·健康医疗
布伦鸽3 小时前
C# Modbus TCP/IP学习记录
开发语言·学习·c#
大模型铲屎官4 小时前
如何用C#继承提升游戏开发效率?Enemy与Boss案例解析
开发语言·unity·c#·游戏引擎·游戏开发·boss·enemy
JosieBook6 小时前
【C#语言】C#文件操作实战:动态路径处理与安全写入
开发语言·c#·io
追逐时光者6 小时前
在 ASP.NET Core 中创建中间件的 4 种方式
后端·.net
云草桑7 小时前
C# .net ai Agent AI视觉应用 写代码 改作业 识别屏幕 标注等
ai·c#·.net·agent
江沉晚呤时7 小时前
深入解析代理模式(Proxy Pattern):设计与应用
安全·c#·系统安全·.netcore
月巴月巴白勺合鸟月半7 小时前
工作记录 2017-03-03
c#·健康医疗
编程乐趣7 小时前
一个纯.Net开发的JavaScript执行引擎
开发语言·javascript·.net