在這個特殊的春節,大家想必都在家出不了們,遠看已經到了回城里上班的日子,但是因為一只蝙蝠的原因導致我們無法回到作業崗位,大家可能有的在家遠程辦公,有些在家躺著看書,有的是在家打游戲;在這個特殊無聊的日志,我果斷從無聊的被窩中 開啟了流量共享wifi 來進行.net core 3.1 源代碼的解讀和學習,并且把學習到的東西分享給大家,
疑問
剛剛接觸ASP.NET CORE 專案的同學可能會有如下疑問:
- ASP.NET CORE 專案的啟動程序是怎么樣的?
- 為什么ASP.NET CORE專案可以在控制臺中運行啟動后變成了一個網站程式?
現在我這里使用.NETCORE 3.1 最新穩定發布版本來進行以上問題的決議,帶大家解決以上問題的疑惑,學習完大家可以對ASP.NETCORE 專案會有一個不一樣的理解和領悟.
啟動程序
剛剛接觸ASP.NET core 的同學們估計都會覺得和之前的ASP.NET 設計大不一樣,代碼風格也有很大的變化,以前的ASP.NET 是全家桶框架模式,里面包含了所有的實作,你用的到的用不到的都集成在里面;然而ASP.NET CORE 框架做了大的改變,以最小化抽象設計,通過擴展方法完成易用性擴展.
解讀過源代碼的同學們都可以發現大多api都是最小化單元抽象介面方式進行設計,其他復雜的方法api都是通過擴展方法進行擴展提供,這也是.NET Core 高效易擴展的一大優勢原因.
對于ASP.NET Core應用程式來說,我們要記住非常重要的一點是:其本質上是一個獨立的控制臺應用,它并不是必需在IIS內部托管且并不需要IIS來啟動運行(而這正是ASP.NET Core跨平臺的基石),ASP.NET Core應用程式擁有一個內置的Self-Hosted(自托管)的Web Server(Web服務器),用來處理外部請求,
不管是托管還是自托管,都離不開Host(宿主),在ASP.NET Core應用中通過配置并啟動一個Host來完成應用程式的啟動和其生命周期的管理,而Host的主要的職責就是Web Server的配置和Pilpeline(請求處理管道)的構建,
我們現在來創建一個ASP.NETCORE WEB 專案 步驟如下
檔案-> 新建 -> 專案 -> 選擇ASP.Net Core Web應用程式 -> 選擇.NETCORE 3.1 框架 如圖:
創建專案后我們從Program 類中可以看到以下代碼:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)//開啟一個默認的通用主機Host建造者
.ConfigureWebHostDefaults(webBuilder =>
{
// 從前綴為“ASPNETCORE” 環境變數加載WEB主機配置
// 默認是Kestrel 設定為web 服務器并對其進行默認配置, 支持iis集成
//注冊系統啟動所使用的組件,比如配置的組件,容器的組件等
webBuilder.UseStartup<Startup>();
});
}
查看以上代碼可以發現 Main 方法中代碼很簡單 ,清晰可見
CreateHostBuilder(args):方法創建了一個IHostBuilder 抽象物件,創建程序包含CreateDefaultBuilder(args):開啟創建一個默認的通用宿主機Host建造者,再通過ConfigureWebHostDefaults()方法配置開啟默認的Kestrel為默認的Web服務器并對其進行默認配置,并集成對iis的集成
Build():負責創建IHost,看過源代碼的同學可以發現Build的程序 會配置各種東西,本身通過管道模式進行了一系列的默認或者自定義的配置以及服務注冊的構建(下面會詳細講解)
Run():啟動IHost
所以,ASP.NET Core應用的啟動本質上是啟動作為宿主的Host物件,
其主要涉及到兩個關鍵物件IHostBuilder和IHost,它們的內部實作是ASP.NET Core應用的核心所在,下面我們就結合原始碼并梳理呼叫堆疊來一探究竟!
源代碼詳細圖如下:

從上圖中我們可以看出CreateDefaultBuilder()方法主要干了五件大事:
UseContentRoot:指定Web host使用的content root(內容根目錄),比如Views,默認為當前應用程式根目錄,ConfigureHostConfiguration:啟動時宿主機需要的環境變數等相關,支持命令列ConfigureAppConfiguration:設定當前應用程式配置,主要是讀取 appsettinggs.json 組態檔、開發環境中配置的UserSecrets、添加環境變數和命令列引數 ,ConfigureLogging:讀取組態檔中的Logging節點,配置日志系統,UseDefaultServiceProvider:設定默認的依賴注入容器,
從圖中可以看出CreateDefaultBuilder 后呼叫了ConfigureWebHostDefaults 方法,該方法默認主要做了以下幾個事情
UseStaticWebAssets:靜態檔案環境的配置啟用UseKestrel:開啟Kestrel為默認的web 服務器.ConfigureServices:服務中間件的注冊,包含路由的中間件的注冊UseIIS:對iis 集成的支持UseStartup:程式Startup 啟動,該啟動類中可以注冊中間件、擴展第三方中間件,以及相關應用程式配置的處理等等操作
現在所有的配置都已經配置創建好了,接下來我們來看看Build 方法主要做了哪些不為人知的事情,先來看下源代碼
/// <summary>
/// Run the given actions to initialize the host. This can only be called once.
/// </summary>
/// <returns>An initialized <see cref="IHost"/></returns>
public IHost Build()
{
if (_hostBuilt)
{
throw new InvalidOperationException("Build can only be called once.");
}
_hostBuilt = true;
BuildHostConfiguration();
CreateHostingEnvironment();
CreateHostBuilderContext();
BuildAppConfiguration();
CreateServiceProvider();
return _appServices.GetRequiredService<IHost>();
}
從代碼中可以發現有一個_hostBuilt 的變數,細心的同學可以發現該變數主要是用于控制是否build 過,所以這里可以大膽猜測只能build 一次該Host;現在看下源代碼決議圖:
經過查看源代碼得到的執行結構如上,因此我把代碼改造成如下結構,
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)//開啟一個默認的通用主機Host建造者
.ConfigureAppConfiguration(config => {
//注冊應用程式內所使用的組態檔,比如資料庫鏈接等等
Console.WriteLine("ConfigureAppConfiguration");
})
.ConfigureServices(service =>
{
//注冊服務中間件等操作
Console.WriteLine("ConfigureServices");
})
.ConfigureHostConfiguration(builder => {
//啟動時需要的組件配置等,比如監聽的埠 url地址等
Console.WriteLine("ConfigureHostCOnfiguration");
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup 代碼如下:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
Console.WriteLine("Startup ");
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
Console.WriteLine("ConfigureServices");
services.AddControllersWithViews();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
Console.WriteLine("Configure");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
通過執行發現執行代碼順序正如我們源代碼的執行順序一致,執行順序如下圖:
為何通過控制臺命令運行后自動啟動了一個網站程式?
以前ASP.NET web專案是需要搭建在iis 中托管運行,但是ASP.NETCORE 專案可以直接通過命令列進行托管運行,運行后可以直接瀏覽器打開,你們有沒有考慮過為什么?,細心的同學查看專案屬性也會發現專案的輸出型別也是控制臺專案,如圖:
查看這圖,有沒有發現很神奇,為什么輸出型別竟然可以通過控制臺命令列進行啟動專案呢?
在上面的源代碼分析程序中可以發現啟動時會啟動一個Kestrel 服務器(ConfigureWebHostDefaults方法中會呼叫UseKestrel),所以命令后啟動一個控制臺應用程式后相當于啟動了一臺web服務器;下面簡要的概括下Kestrel 服務器的優勢:
Kestrel:Kestrel 是個精簡高效的HttpServer,以包形式提供,自身不能單獨運行,
內部封裝了對 libuv 的呼叫,作為I/O底層,屏蔽各系統底層實作差異;有了Kestrel才能真正的實作跨平臺.
好了,想必同學們到這里已經對上面 兩個疑惑有了清晰的答案了,這里我拋出一個疑問,看了上面的代碼解讀,大家有沒有發現ASP.NET CORE 和ASP.NET 有了很大的不同,這是什么樣的設計改進呢?敬請期待下期我們一起來學習ASP.NET CORE 的牛逼的管道模型.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/69621.html
標籤:其他
下一篇:C#面向物件--屬性
