一、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
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)上图中的next
和 context
都是传入的参数,但它们的来源和作用不同。
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 = 2
,Middleware3
)
cs
app = Middleware3.Invoke(app);
-
在第一次
for
循环时,app
(后面的那个)是初始化的默认 404 处理逻辑。 -
Middleware3.Invoke(app)
会将当前的app
(即默认的 404 处理逻辑)作为next
参数传递给Middleware3
。 -
Middleware3
返回一个新的RequestDelegate
,这个委托会执行Middleware3
的逻辑,但不会调用next
(即默认的 404 处理逻辑)。

2》第二次循环(c = 1
,Middleware2
)
cs
app = Middleware2.Invoke(app);
-
app
(后面的那个)现在是Middleware3
返回的RequestDelegate
。 -
app
(后面的那个) 被传递给Middleware2
的Invoke
方法。 -
Middleware2
返回一个新的RequestDelegate
,这个委托会先执行Middleware2
的前逻辑,然后调用app
(即Middleware3
的逻辑),最后执行Middleware2
的后逻辑。

3》第三次循环(c = 0
,Middleware1
)
cs
app = Middleware1.Invoke(app);
-
app
(后面的那个)现在是Middleware2
返回的RequestDelegate
。 -
app
(后面的那个)被传递给Middleware1
的Invoke
方法。 -
Middleware1
返回一个新的RequestDelegate
,这个委托会先执行Middleware1
的前逻辑,然后调用app
(即Middleware2
的逻辑),最后执行Middleware1
的后逻辑。

4>综上,这就是俄罗斯套娃的由来
1》 如下图,第一次循环是最里面那个圈(对应第三个中间件)。
嵌套的委托,体现在里面的圈 被 外面的圈 包围。
Http管道请求从下图的左到右,每个中间件的前 逻辑 体现在 请求开始(下图蓝线前)接触到的线;后逻辑 体现在 请求在(下图蓝线后)接触到的线

2》对应下图,就是管道先添加进去的第一次循环(第三个中间件),是最里面绿色的那圈。
区别代码顺序:配置管道的过程(代码从上到下)=下列的每个圈(从外画到内),管道执行过程=下图的箭头
