ASP.NET Core 框架本质学习
本文作為學(xué)習(xí)過程中的一個記錄。
學(xué)習(xí)文章地址:
https://www.cnblogs.com/artech/p/inside-asp-net-core-framework.html
一. ASP.NET Core 框架上的 Hello World程序
public class Program{
public static void Main()
=> new WebHostBuilder()
.UseKestrel()
.Configure(app => app.Run(context => context.Response.WriteAsync("Hello World!")))
.Build()
.Run();
}
WebHost :承載Web應(yīng)用的宿主;
WebHostBuilder :WebHost的構(gòu)建者;
而 在WebHostBuilder在調(diào)用 Build方法之前,調(diào)用的 兩個方法:
UseKestrel :旨在注冊一個名為Kestrel的服務(wù)器
Configure:為了注冊一個用來處理請求的中間件
在上面的代碼中,中間件在響應(yīng)的主體內(nèi)容中寫入了一個 Hello World 的文本。
當(dāng)我們在調(diào)用Run方法啟動作為應(yīng)用宿主的 WebHost的時候,WebHost會利用WebHostBuilder提供的服務(wù)器和中間件構(gòu)建一個請求處理管道。?
而下面主要講的就是 這個管道是如何被構(gòu)建起來的,以及該管道采用怎樣的請求處理流程。
二. 在我們自己的ASP.NET Core Mini上面開發(fā)的 Hello World
本文作為學(xué)習(xí)過程中的一個記錄。
學(xué)習(xí)文章地址:
https://www.cnblogs.com/artech/p/inside-asp-net-core-framework.html
一. ASP.NET Core 框架上的 Hello World程序
public class Program{
public static void Main()
=> new WebHostBuilder()
.UseKestrel()
.Configure(app => app.Run(context => context.Response.WriteAsync("Hello World!")))
.Build()
.Run();
}
WebHost :承載Web應(yīng)用的宿主;
WebHostBuilder :WebHost的構(gòu)建者;
而 在WebHostBuilder在調(diào)用 Build方法之前,調(diào)用的 兩個方法:
UseKestrel :旨在注冊一個名為Kestrel的服務(wù)器
Configure:為了注冊一個用來處理請求的中間件
在上面的代碼中,中間件在響應(yīng)的主體內(nèi)容中寫入了一個 Hello World 的文本。
當(dāng)我們在調(diào)用Run方法啟動作為應(yīng)用宿主的 WebHost的時候,WebHost會利用WebHostBuilder提供的服務(wù)器和中間件構(gòu)建一個請求處理管道。?
而下面主要講的就是 這個管道是如何被構(gòu)建起來的,以及該管道采用怎樣的請求處理流程。
二. 在我們自己的ASP.NET Core Mini上面開發(fā)的 Hello World
代碼說明:
在創(chuàng)建出 WebHostBuilder 之后,我們調(diào)用了它的擴展方法 UseHttpListener 注冊了一個自定義的基于 HttpListener的服務(wù)器;
隨后針對 Configure 方法的調(diào)用中,我們注冊了三個中間件。
由于中間件最終是通過 Delegate對象來體現(xiàn)的,所以我們可以將中間件定義成與Delegate類型具有相同簽名的方法。
程序運行后,得到的輸出結(jié)果:
三. 自定義的ASP.NET Core Mini框架講解
下面主要是對 ASP.NET Core Mini框架的構(gòu)建過程中關(guān)鍵部分的講解。
主要涉及 HttpContext、RequestDelegate、Middleware、ApplicationBuilder、Server、WebHost、WebHostBuilder 等七個對象;
另外 會講到 HttpContext與Server之間的適配;HttpListenerServer等;
1. 第一個對象:HttpContext
關(guān)于 HttpContext的本質(zhì),還得從請求處理管道的層面來講。
對于由一個服務(wù)器和多個中間件構(gòu)建的管道來說,面向傳輸層的服務(wù)器負(fù)責(zé)請求的監(jiān)聽、接收和最終的響應(yīng);
當(dāng)它接收到客戶端發(fā)送的請求后,需要將它分發(fā)給后續(xù)中間件進行處理。
對于某個中間件來說,當(dāng)我們完成了自身的請求處理任務(wù)之后,在大部分情況下,也需要將請求分發(fā)給后續(xù)的中間件。
請求在服務(wù)器與中間件之間,以及在中間件之間的分發(fā)是通過共享上下文的方式實現(xiàn)的。
( 如上圖,當(dāng)服務(wù)器接收到請求之后,會創(chuàng)建一個通過HttpContext表示的上下文對象,所有中間件都是在這個上下文中處理請求的;
那么一個HttpContext對象究竟攜帶了怎樣的上下文信息呢?
我們知道一個HTTP事務(wù)具有非常清晰的界定,即接收請求、發(fā)送響應(yīng);
所以請求和響應(yīng)是兩個基本的要素,也是HttpContext承載的最核心的 上下文信息。)
故,HttpContext的核心要素:請求和響應(yīng)
2. 第二個對象:RequestDelegate
這是一個委托,也需要從管道的角度才能充分理解這個委托對象的本質(zhì)。
?2.1 管道的設(shè)計
可以總結(jié)為?Pipeline = Server + Middlewares? ,再精簡寫的話,可以寫為?Pipeline = Server + HttpHandler .?
2.2 那么,我們?nèi)绾蝸肀磉_(dá)HttpHandler呢?
既然針對當(dāng)前請求的所有輸入和輸出都通過HttpContext來表示,那么 HttpHandler就可以表示成一個 Action<HttpContext>對象。
那么HttpHandler在ASP.NET Core中時通過 Action<HttpContext>來表示的嗎?
其實不是的,原因很簡單:Action<HttpContext>只能表示針對請求的?同步的處理操作,但是針對 HTTP 請求既可以是同步的,也可以是異步的,更多的其實是異步的。
那么在 .NET Core的世界中如何來表示一個同步或者異步操作呢?就是Task對象,那么 HttpHandler自然可以表示為一個 Func<HttpContext,Task>對象。
由于這個委托對象實在太重要了,所以我們將它定義成一個獨立的類型:delegate Task RequestDelegate(HttpContext context)?。
3. 第三個對象:Middleware
中間件在ASP.NET Core 中被表示成一個 Func<RequestDelegate,RequestDelegate>對象,即它的輸入和輸出都是一個RequestDelegate。
為什么采用一個Func<RequestDelegate,RequestDelegate>對象來表示中間件。是因為這樣的考慮:
對于管道中的某一個中間件來說,由后續(xù)中間件組成的管道體現(xiàn)為一個RequestDelegate對象,由于當(dāng)前中間件在完成了自身的請求處理任務(wù)之后,往往需要將請求分發(fā)給后續(xù)中間件進行處理,所以它需要將由后續(xù)中間件構(gòu)成的RequestDelegate作為輸入。
即:上一個中間件的輸出需要可以作為下一個中間件的輸入,所以設(shè)計為Func<RequestDelegate,RequestDelegate>對象
4. 第四個對象:ApplicationBuilder
ApplicationBuilder 是用來構(gòu)建 Application的。
既然 Pipeline = Server + HttpHandler , 可以看出HttpHandler承載了當(dāng)前應(yīng)用的所有職責(zé),那么 HttpHandler就等于 Application。
由于 HttpHandler通過RequestDelegate表示,那么由ApplicationBuilder構(gòu)建的Application就是一個RequestDelegate對象。(職責(zé)1)
由于表示HttpHandler的RequestDelegate是由注冊的中間件來構(gòu)建的,所以ApplicationBuilder還具有注冊中間件的功能。(職責(zé)2)
基于ApplicationBuilder具有的這兩個基本職責(zé),我們可以將對應(yīng)的接口定義為如下形式。
Use 方法用來注冊提供的中間件,Builder方法則將注冊的中間件構(gòu)建成一個RequestDelegate對象。
public interface IApplicationBuilder{
IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
RequestDelegate Build();
}
下面是針對這個接口的具體實現(xiàn)。
我們用一個列表保存注冊的中間件,所以Use方法只需要將提供的中間件添加到這個列表中即可。
當(dāng)Build方法被調(diào)用后,我們只需要按照與注冊相反的順序依次執(zhí)行表示中間件的Func<RequestDelegate,RequestDelegate>對象,就能最終構(gòu)建出代表HttpHandler的RequestDelegate對象。
在調(diào)用第一個中間件(最后注冊)的時候,我們創(chuàng)建了一個RequestDelegate作為輸入,后者會將響應(yīng)狀態(tài)碼設(shè)置為404。
所以如果ASP.NET Core應(yīng)用在沒有注冊任何中間件的情況下,總是返回一個404響應(yīng)。
如果所有中間件在完成了自身的請求處理任務(wù)之后都選擇將請求向后分發(fā),同樣會返回一個404響應(yīng)。
總結(jié):對于上面的四個對象,從后向前依次對前一個進行包裝。
5. 第五個對象:Server
當(dāng)我們運行(Run)作為應(yīng)用宿主的WebHost的時候,服務(wù)器它被自動啟動。
啟動后的服務(wù)器會綁定到指定的端口進行請求監(jiān)聽,一旦有請求抵達(dá),服務(wù)器會根據(jù)該請求創(chuàng)建出代表上下文的HttpContext對象,
并將該上下文作為輸入,調(diào)用由所有注冊中間件構(gòu)建而成的RequestDelegate對象。
簡單起見,我們使用如下簡寫的IServer接口來表示服務(wù)器。
我們通過定義在IServer接口的唯一方法 StartAsync啟動服務(wù)器,
作為參數(shù)的?handler 正是由所有中間件共同構(gòu)建而成的RequestDelegate對象
public interface IServer{
Task StartAsync(RequestDelegate handler);
}
6. HttpContext和Server之間的適配
面向應(yīng)用層的HttpContext對象是對請求和相應(yīng)的封裝,但是請求最初來源于服務(wù)器,針對HttpContext的任何響應(yīng)操作也必須作用于當(dāng)前的服務(wù)器才能真正起作用。
現(xiàn)在問題來了,所有的ASP.NET Core應(yīng)用使用的都是同一個HttpContext類型,但是卻可以注冊不同類型的服務(wù)器,我們必須解決兩者之間的適配問題。
同一個HttpContext類型與不同服務(wù)器類型之間的適配可以通過添加一個抽象層來解決,我們定義該層的對象為Feature。
如上圖,我們可以定義一系列的Feature接口來為HttpContext提供上下文信息,其中最重要的就是提供請求的 IRequestFeature和完成響應(yīng)的IResponseFeature接口。
那么具體的服務(wù)器只需要實現(xiàn)這些Feature接口就可以了。
下面是一些代碼片段。我們定義了一個IFeatureCollection接口來表示存放Feature對象的集合。
為了編程上的便利,我們定義了兩個擴展方法 Set<T>和Get<T>來設(shè)置和獲取Feature對象。
如下,用來提供請求的IHttpRequestFeature和提供響應(yīng)IHttpResponseFeature接口的定義,可以看出它們具有和HttpRequest和HttpResponse完全一致的成員定義。
接下來,我們來看看HttpContext的具體實現(xiàn)。
ASP.NET Core Mini的HttpContext只包含Request和Response兩個屬性成員,對應(yīng)的類型分別為HttpRequest和HttpResponse,下面是這兩個類型的具體實現(xiàn)。
其中,HttpRequest和HttpResponse都是通過一個IFeatureCollection對象構(gòu)建而成的,它們對應(yīng)的屬性成員均由包含在這個Feature集合中的IHttpRequestFeature和IHttpResponseFeature對象來提供。
HttpContext的實現(xiàn)就更加簡單了。我們在創(chuàng)建一個HttpContext對象時同樣會提供一個IFeatureCollection對象,
我們利用該對象創(chuàng)建對應(yīng)的HttpRequest和HttpResponse對象,并作為其對應(yīng)的屬性值。
總結(jié):在HttpContext中傳入 IFeatureCollection對象,HttpContext中的成員對象HttpRequest和HttpResponse會利用這個IFeatureCollection來被構(gòu)建。從而完成HttpContext的構(gòu)建。當(dāng)然,其中少不了需要Server部分,下面會講。
7. HttpListenerServer 服務(wù)器
在對服務(wù)器和它與HttpContext的適配原理有清晰的認(rèn)識之后,我們嘗試著定義一個服務(wù)器。
在前面,我們利用WebHostBuilder的擴展方法UseHttpListener注冊了一個HttpListenerServer,我們現(xiàn)在來看看這個采用HttpListener作為監(jiān)聽器的服務(wù)器類型是如何實現(xiàn)的。
由于所有的服務(wù)器都需要有自己的Feature實現(xiàn)來為HttpContext提供對應(yīng)的上下文信息,所以我們得先來為HttpListenerServer定義相應(yīng)的接口。
對HttpListener監(jiān)聽器稍微了解的朋友應(yīng)該知道它在接收到請求之后同時會創(chuàng)建一個自己的上下文對象,對應(yīng)的類型為HttpListenerContext。
如果采用HttpListenerServer作為應(yīng)用的服務(wù)器,意味著HttpContext承載的上下文信息最初來源于這個HttpListenerContext,所以Feature的目的旨在解決這兩個上下文之間的適配問題。
總結(jié):Feature實際上解決的就是HttpContext和HttpListenerContext之間的適配問題。
下面的HttpListenFeature就是我們?yōu)镠ttpListenerServer定義的Feature。HttpListenerFeature同時實現(xiàn)了IHttpRequestFeature和IHttpResponseFeature,實現(xiàn)的 6 個屬性最初都來源于創(chuàng)建該對象提供的HttpListenerContext對象。
當(dāng)HttpListener監(jiān)聽到抵達(dá)的請求后,我們會得到一個HttpListenerContext對象,此時我們只需要據(jù)此創(chuàng)建一個HttpListenerFeature對象,
并且它分別以IHttpRequestFeature和IHttpResponseFeature接口類型注冊到創(chuàng)建FeatureCollection集合上。
我們最終利用這個FeatureCollection對象創(chuàng)建出代表上下文的HttpContext,然后將它作為參數(shù)調(diào)用由所有中間件公共構(gòu)建的RequestDelegate對象即可。
8. 第六個對象:WebHost
到目前為止,我們已經(jīng)知道了由一個服務(wù)器和多個中間件構(gòu)成的管道是如何完整針對請求的監(jiān)聽、接收、處理和最終響應(yīng)的,接下來討論這樣的管道是如何被構(gòu)建出來的。
管道是在作為應(yīng)用宿主的WebHost對象啟動的時候被構(gòu)建出來的,在ASP.NET Core Mini 中,
我們將表示應(yīng)用宿主的IWebHost接口簡寫成如下形式:
只包含一個StartAsync方法來啟動應(yīng)用程序。
public interface IWebHost{
Task StartAsync();
}
由于由WebHost構(gòu)建的管道由Server和HttpHandler構(gòu)成,我們在默認(rèn)實現(xiàn)的WebHost類型中,我們直接提供這兩個對象。
在實現(xiàn)的StartAsync方法中,我們只需要將后者作為參數(shù)調(diào)用前者的StartAsync方法將服務(wù)器啟
9. 第七個對象:WebHostBuilder
WebHost的作用:就是創(chuàng)建作為應(yīng)用宿主的WebHost。
由于在創(chuàng)建WebHost的時候,需要提供注冊的服務(wù)器和由所有注冊中間件構(gòu)建而成的RequestDelegate,
所以在對應(yīng)接口IWebHostBuilder中,我們?yōu)樗x了三個核心方法。
public interface IWebHostBuilder{
IWebHostBuilder UseServer(IServer server);
IWebHostBuilder Configure(Action<IApplicationBuilder> configure);
IWebHost Build();
}
除了用來創(chuàng)建WebHost的Build方法之外,我們提供了用來注冊服務(wù)器的UseServer方法和用來注冊中間件的Configure方法。
Configure方法提供了一個類型為 Action<IApplicationBuilder>的參數(shù),
意味著我們針對中間件的注冊時利用上面介紹的IApplicationBuilder對象來完成的。
如下代碼,WebHostBuilder是針對IWebHostBuilder接口的默認(rèn)實現(xiàn),
它具有兩個字段分別用來保存注冊的中間件和調(diào)用Configure方法提供的Action<IApplicationBuilder>對象。
當(dāng)Build方法被調(diào)用之后,我們創(chuàng)建一個ApplicationBuilder對象,并將它作為參數(shù)調(diào)用這些Action<IApplicationBuilder>委托,
進而將所有中間件全部注冊到這個ApplicationBuilder對象上。
我們最終調(diào)用它的Build方法得到所有中間件共同構(gòu)建的RequestDelegate對象,并利用它和注冊的服務(wù)器構(gòu)建作為應(yīng)用宿主的WebHost對象。
至此,本篇結(jié)束。
補充:
這里補充的是按照這里的學(xué)習(xí),實現(xiàn)這個程序的過程。
1. 首先 創(chuàng)建 Feature.cs 文件
里面定義了請求和響應(yīng)里面需要的一些內(nèi)容。
2. 創(chuàng)建?FeatureCollection.cs 文件
這個應(yīng)該就是 用來裝 請求和響應(yīng) 的。
注:1,2兩個文件中的接口或者類,不依賴其他自定義類
3. 創(chuàng)建?HttpContext.cs 文件
這個里面定義了 請求實體類,響應(yīng)實體類,共享上下文(HttpContext), 以及響應(yīng)的擴展方法輸出內(nèi)容。
4.創(chuàng)建?RequestDelegate.cs 文件
這個用來承載 共享上下文 HttpContext 的。
5. 創(chuàng)建?ApplicationBuilder.cs 文件
這個是用來承載 RequestDelegate 的,并且存放中間件及使之串起來的。
6. 創(chuàng)建?IServer.cs 文件
這個是用來定義服務(wù)器接口的。
7.創(chuàng)建?HttpListenerFeature.cs 文件
我們的共享上下文信息最初來源于這個類中的 HttpListenerContext。它是在服務(wù)器中,用來接收 HttpListener 中的上下文對象的(即HttpListenerContext)。
這里把 它按 請求和響應(yīng)的接口定義進行拆分。
8. 創(chuàng)建?HttpListenerServer.cs 文件
這個是服務(wù)器文件,其中定義了監(jiān)聽器,監(jiān)聽的url集合,及啟動監(jiān)聽,創(chuàng)建共享上下文,及使用RequestDelegate類型的處理器承載 上下文等操作。
9. 創(chuàng)建?WebHost.cs 文件
這里是用來創(chuàng)建宿主的,用來把服務(wù)器和中間件服務(wù)連接起來的。
10. 創(chuàng)建?WebHostBuilder.cs 文件
這個是宿主構(gòu)建者,用來設(shè)置服務(wù)器,配置中間件,以及 Build 出宿主WebHost的。
11. 最后,創(chuàng)建?Program.cs 文件
這其中包括 創(chuàng)建宿主構(gòu)建者,設(shè)置服務(wù)器,配置中間件, Build成宿主,及啟動宿主等。
代碼結(jié)束!!
12. 上面的代碼實際已經(jīng)結(jié)束了,但是發(fā)現(xiàn)編譯的時候報錯。
C# 7.0 不支持Program.cs中的用法。
怎么修改呢?
右鍵項目---> 屬性----> 生成 ----> 高級 ---->?
然后在 常規(guī) 下的語言版本中,選擇 c#最新次要版本?
如下
13. 重新編譯,成功
14. 運行,然后在 瀏覽器中 輸入?http://localhost:5000
可以在代碼中打上斷點,觀察執(zhí)行過程。
這里,把我自己寫的也上傳到github了,方便自己查閱,有疑問的小伙伴可以自己去原文學(xué)習(xí)
我的github地址:https://github.com/Vincent-yuan/asp.net_core_mini
原文鏈接:https://www.cnblogs.com/Vincent-yuan/p/11318718.html
.NET社區(qū)新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?
總結(jié)
以上是生活随笔為你收集整理的ASP.NET Core 框架本质学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SonarQube系列一、Linux安装
- 下一篇: asp.net ajax控件工具集 Au