一、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)上圖中的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。它們的添加順序如下:
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
)
app = Middleware3.Invoke(app);
-
在第一次?
for
?循環時,app
?(后面的那個)是初始化的默認 404 處理邏輯。 -
Middleware3.Invoke(app)
?會將當前的?app
(即默認的 404 處理邏輯)作為?next
?參數傳遞給?Middleware3
。 -
Middleware3
?返回一個新的?RequestDelegate
,這個委托會執行?Middleware3
?的邏輯,但不會調用?next
(即默認的 404 處理邏輯)。
2》第二次循環(c = 1
,Middleware2
)
app = Middleware2.Invoke(app);
-
app
(后面的那個)現在是?Middleware3
?返回的?RequestDelegate
。 -
app
(后面的那個)?被傳遞給?Middleware2
?的?Invoke
?方法。 -
Middleware2
?返回一個新的?RequestDelegate
,這個委托會先執行?Middleware2
?的前邏輯,然后調用?app
(即?Middleware3
?的邏輯),最后執行?Middleware2
?的后邏輯。
3》第三次循環(c = 0
,Middleware1
)
app = Middleware1.Invoke(app);
-
app
?(后面的那個)現在是?Middleware2
?返回的?RequestDelegate
。 -
app
?(后面的那個)被傳遞給?Middleware1
?的?Invoke
?方法。 -
Middleware1
?返回一個新的?RequestDelegate
,這個委托會先執行?Middleware1
?的前邏輯,然后調用?app
(即?Middleware2
?的邏輯),最后執行?Middleware1
?的后邏輯。
?4>綜上,這就是俄羅斯套娃的由來
1》 如下圖,第一次循環是最里面那個圈(對應第三個中間件)。
????????嵌套的委托,體現在里面的圈 被 外面的圈 包圍。
????????Http管道請求從下圖的左到右,每個中間件的前邏輯 體現在 請求開始(下圖藍線前)接觸到的線;后邏輯 體現在 請求在(下圖藍線后)接觸到的線
2》對應下圖,就是管道先添加進去的第一次循環(第三個中間件),是最里面綠色的那圈。
區別代碼順序:配置管道的過程(代碼從上到下)=下列的每個圈(從外畫到內),管道執行過程=下圖的箭頭