1. 入口檔案
一個應用程式總有一個入口檔案,是應用啟動程式碼開始執行的地方,這裡往往也會涉及到應用的各種配置。當我們接觸到一個新框架的時候,可以從入口檔案入手,瞭解入口檔案,能夠幫助我們更好地理解應用的相關配置以及應用的工作方式。
.Net Core 應用的入口檔案是 Program.cs,這裡是應用啟動的地方。在 .Net 6 之前的版本,Program.cs 檔案是下面這樣的,這是建立一個 Web 專案時的預設程式碼。
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
其中 Main 方法就是應用啟動的入口。可以看到在應用啟動的時候,透過 建造者模式 建立了一個主機,並進行了相關的配置,最後將其執行起來。
從程式碼中可以看到,在對主機進行配置的時候,使用到了 Startup 類,在 .Net 6 之前的版本,Startup 類承擔應用的啟動任務,是應用配置的主要地方。
2. Startup 類
2.1 Startup類結構
Startup 類支援兩種定義方式,一種是實現 IStartup 介面,一種是基於約定的。無論哪一種,Startup 類的基本結構都包含以下兩個個關鍵函式。整體來說,基於約定的 Startup 類更加靈活。
- ConfigureServices方法
- Configure方法
2.1.2 ConfigureServices 方法
- 該方法是可選的
- 該方法用於新增服務到DI容器中
- 該方法在 Configure 方法之前被呼叫
- 基於約定的情況下,該方法要麼無引數,要麼只能有一個引數且型別必須為 IServiceCollection
- 該方法內的程式碼大多是形如
Add{Service}
的擴充套件方法
2.1.3 Configure方法
- 該方法是必須的
- 該方法用於配置 HTTP 請求管道,透過向管道新增中介軟體,應用不同的響應方式。
- 該方法在 ConfigureServices 方法之後被呼叫
- 基於約定的情況下,該方法中的引數可以接受任何已注入到DI容器中的服務
- 該方法內的程式碼大多是形如
Use{Middleware}
的擴充套件方法 - 該方法內中介軟體的註冊順序與程式碼的書寫順序是一致的,先註冊的先執行,後註冊的後執行
另外還有建構函式,當使用通用主機時,Startup 建構函式支援注入以下三種服務型別,在 Startup 類中全域性進行使用:
- IConfiguration
- IWebHostEnvironment
- IHostEnvironment
2.2 預設Startup
所謂預設Startup,就是應用啟動配置不用 Startup 類,直接在 ConfigureWebHostDefaults 中進行配置。
publicstaticIHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
// ConfigureServices 可以呼叫多次,最終會將結果聚合
webBuilder.ConfigureServices(services =>
{
})
// Configure 如果呼叫多次,則只有最後一次生效.
.Configure(app =>
{
// Configure呼叫之前,ConfigureServices已經呼叫,容器物件已經生成,所以這裡可以透過容器直接解析需要的物件
var env = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>();
});
});
2.3 多環境配置
.NET Core 框架支援多環境開發,可以透過環境變數 ASPNETCORE_ENVIRONMENT
來設定應用當前的執行環境,以實現一套程式碼在不同環境下執行,根據環境區分一定的行為,支援開發、測試、預釋出、生成環境下不同條件、不同配置的執行場景。
我們可以直接在機器的環境變數中進行設定,在專案的 Properties 資料夾裡面的“launchSettings.json”檔案進行配置,該檔案是用於配置VS中專案啟動的,在 profiles 節點中透過不同的 json 物件配置當前應用的啟動模式,而描述啟動模式的 json 物件支援的欄位中有一個 environmentVariables
節點,可以透過鍵值對方式配置環境變數。
這裡配置的環境變數只會在當前專案中起作用。若應用執行環境中從未對 ASPNETCORE_ENVIRONMENT 環境變數進行配置,則預設為 Production 。而我們其實可以將 ASPNETCORE_ENVIRONMENT 設定為任意值。
之後,這些環境變數會在主機初始化的時候作為主機配置被載入到應用中,這些會在後面的配置系統中詳細講到。而在程式碼中,我們可以透過注入 IWebHostEnvironment 服務獲取到當前應用的執行環境。例如下面在 StartUp 中透過判斷環境執行不同的應用初始化邏輯。
IWebHostEnvironment 服務中預設提供對 Development、Production、Staging 三種環境進行判斷的擴充套件方法,如果是其他自定的環境,如 Test,可以使用 IsEnviroment() 方法進行判斷。
透過 IWebHostEnvironment 判斷不同環境,從而在 StartUp 類中使用不同的初始化初始化邏輯,這種方式適合於不同環境下程式碼差異較少的情況。除此之外還有兩種基於約定的方式,分別是 Startup 方法約定 和 StartUp 類名約定 。
StartUp 方法約定具體是指 StartUp 類中 ConfigureServices 和 Configure 方法還可以按照Configure{EnvironmentName}Services和Configure{EnvironmentName}Services 這樣的命名格式來寫,透過命名約定的 {EnvironmentName} 部分割槽分不同環境,裝載不同環境的程式碼。
如果 StartUp 類中存在與當前環境名稱匹配的 Configure{EnvironmentName}Services和Configure{EnvironmentName}Services 方法的話,則應用啟動時會執行相應的方法中的邏輯,如果沒有則執行原始的 ConfigureServices 和 Configure 方法中的邏輯。
透過檢視原始碼,可以看到當我們明確配置一個 Startup 類作為應用啟動類的時候,會先判斷是否是實現了 IStartup 介面。
如果沒有的話,則透過 StartupLoader 判斷 Startup 類是否符合約定,最終構建出實現了 IStartup 介面的ConventionBasedStartup,並注入到容器中。這時候會結合環境變數,優先獲取帶有環境變數的方法,如果沒有則使用沒有帶環境變數的方法。
而 StartUp 類名約定和方法約定類似,程式啟動時,會優先尋找當前環境命名符合Startup{EnvironmentName}的 Startup 類,如果找不到,則使用名稱為Startup的類。類名約定的方式適用於多環境下,程式碼差異較大的情況。
類名約定的方式下,在配置使用 UseStartUp 的時候需要一點小改動:
檢視原始碼,可以看到在我們呼叫上面的方法的時候,實際上並沒有做具體的 Startup 類的構建操作,只是寫入了兩個設定,其實就是寫入到了配置系統中,其中 WebHostDefaults.StartupAssemblyKey 是關鍵。
之後在我們應用啟動,呼叫Build方法時,在構建ASP.NET Core 基本服務的時候才會根據設定去構建啟動類。
在這裡透過 StartupLoader 結合環境名稱查詢程式集中符合約定的 Startup 類。
參考文章:
ASP.NET Core 系列總結:
目錄:ASP.NET Core 系列總結
下一篇:ASP.NET Core - IStartupFilter與IHostingStartup