理解ASP.NET Core 啟動類(Startup)
準備工作:一份ASP.NET Core Web API應用程序
當我們來到一個陌生的環(huán)境,第一件事就是找到廁所在哪。
當我們接觸一份新框架時,第一件事就是找到程序入口,即Main方法
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>(); }); }
代碼很簡單,典型的建造者模式:通過IHostBuilder
創(chuàng)建一個通用主機(Generic Host)
,然后啟動它(至于什么是通用主機,咱們后續(xù)的文章會說到)。咱們不要一上來就去研究CreateDefaultBuilder
、ConfigureWebHostDefaults
這些方法的源代碼,應該去尋找能看的見、摸得著的,很明顯,只有Startup
。
Startup類
Startup
類承擔應用的啟動任務,所以按照約定,起名為Startup
,不過你可以修改為任意類名(強烈建議類名為Startup)。
默認的Startup
結構很簡單,包含:
- 構造函數(shù)
Configuration
屬性ConfigureServices
方法Configure
方法
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. // 該方法由運行時調用,使用該方法向DI容器添加服務 public void ConfigureServices(IServiceCollection services) { } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // 該方法由運行時調用,使用該方法配置HTTP請求管道 public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { } }
Startup構造函數(shù)
當使用通用主機(Generic Host)時,Startup構造函數(shù)支持注入以下三種服務類型:
IConfiguration
IWebHostEnvironment
IHostEnvironment
public Startup( IConfiguration configuration, IHostEnvironment hostEnvironment, IWebHostEnvironment webHostEnvironment) { Configuration = configuration; HostEnvironment = hostEnvironment; WebHostEnvironment = webHostEnvironment; } public IConfiguration Configuration { get; } public IHostEnvironment HostEnvironment { get; set; } public IWebHostEnvironment WebHostEnvironment { get; set; }
這里你會發(fā)現(xiàn) HostEnvironment
和 WebHostEnvironment
的實例是同一個。別著急,后續(xù)文章我們聊到Host的時候,你就明白了。
ConfigureServices
- 該方法是可選的
- 該方法用于添加服務到DI容器中
- 該方法在
Configure
方法之前被調用 - 該方法要么無參數(shù),要么只能有一個參數(shù)且類型必須為
IServiceCollection
- 該方法內的代碼大多是形如
Add{Service}
的擴展方法
常用的服務有(部分服務框架已默認注冊):
AddControllers
:注冊Controller相關服務,內部調用了AddMvcCore
、AddApiExplorer
、AddAuthorization
、AddCors
、AddDataAnnotations
、AddFormatterMappings
等多個擴展方法AddOptions
:注冊Options相關服務,如IOptions<>
、IOptionsSnapshot<>
、IOptionsMonitor<>
、IOptionsFactory<>
、IOptionsMonitorCache<>
等。很多服務都需要Options,所以很多服務注冊的擴展方法會在內部調用AddOptions
AddRouting
:注冊路由相關服務,如IInlineConstraintResolver
、LinkGenerator
、IConfigureOptions<RouteOptions>
、RoutePatternTransformer
等AddAddLogging
:注冊Logging相關服務,如ILoggerFactory
、ILogger<>
、IConfigureOptions<LoggerFilterOptions>>
等AddAuthentication
:注冊身份認證相關服務,以方便后續(xù)注冊JwtBearer、Cookie等服務AddAuthorization
:注冊用戶授權相關服務AddMvc
:注冊Mvc相關服務,比如Controllers、Views、RazorPages等AddHealthChecks
:注冊健康檢查相關服務,如HealthCheckService
、IHostedService
等
Configure
- 該方法是必須的
- 該方法用于配置HTTP請求管道,通過向管道添加中間件,應用不同的響應方式。
- 該方法在
ConfigureServices
方法之后被調用 - 該方法中的參數(shù)可以接受任何已注入到DI容器中的服務
- 該方法內的代碼大多是形如
Use{Middleware}
的擴展方法 - 該方法內中間件的注冊順序與代碼的書寫順序是一致的,先注冊的先執(zhí)行,后注冊的后執(zhí)行
常用的中間件有
UseDeveloperExceptionPage
:當發(fā)生異常時,展示開發(fā)人員異常信息頁。如圖
UseRouting
:路由中間件,根據(jù)Url中的路徑導航到對應的Endpoint。必須與UseEndpoints
搭配使用。UseEndpoints
:執(zhí)行路由所選擇的Endpoint對應的委托。UseAuthentication
:身份認證中間件,用于對請求用戶的身份進行認證。比如,早晨上班打卡時,管理員認出你是公司員工,那么才允許你進入公司。UseAuthorization
:用戶授權中間件,用于對請求用戶進行授權。比如,雖然你是公司員工,但是你是一名.NET開發(fā)工程師,那么你只允許坐在.NET開發(fā)工程師區(qū)域的工位上,而不能坐在老總的辦公室里。UseMvc
:Mvc中間件。UseHealthChecks
:健康檢查中間件。UseMiddleware
:用來添加匿名中間件的,通過該方法,可以方便的添加自定義中間件。
省略Startup類
另外,Startup
類也可以省略,直接進行如下配置即可(雖然可以這樣做,但是不推薦):
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { // ConfigureServices 可以調用多次,最終會將結果聚合 webBuilder.ConfigureServices(services => { }) // Configure 如果調用多次,則只有最后一次生效 .Configure(app => { var env = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>(); }); });
IStartupFilter
public interface IStartupFilter { Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next); }
有時,我們想要將一系列相關中間件的注冊封裝到一起,那么我們只需要通過實現(xiàn)IStartupFilter
,并在Startup.ConfigureServices
中配置IStartupFilter
的依賴注入即可。
- 在
IStartupFilter
中配置的中間件,總是比Startup
類中Configure
方法中的中間件先注冊;對于多個IStartupFilter
實現(xiàn),執(zhí)行順序與服務注冊時的順序一致
我們可以通過一個例子來驗證一下中間件的注冊順序。
首先是三個IStartupFilter
的實現(xiàn)類:
public class FirstStartupFilter : IStartupFilter { public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next) => app => { app.Use((context, next) => { Console.WriteLine("First"); return next(); }); next(app); }; } public class SecondStartupFilter : IStartupFilter { public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next) => app => { app.Use((context, next) => { Console.WriteLine("Second"); return next(); }); next(app); }; } public class ThirdStartupFilter : IStartupFilter { public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next) => app => { app.Use((context, next) => { Console.WriteLine("Third"); return next(); }); next(app); }; }
接下來進行注冊:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureServices(services => { // 第一個被注冊 services.AddTransient<IStartupFilter, FirstStartupFilter>(); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }) .ConfigureServices(services => { // 第三個被注冊 services.AddTransient<IStartupFilter, ThirdStartupFilter>(); }); public class Startup { public void ConfigureServices(IServiceCollection services) { // 第二個被注冊 services.AddTransient<IStartupFilter, SecondStartupFilter>(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 第四個被注冊 app.Use((context, next) => { Console.WriteLine("Forth"); return next(); }); } }
最后通過輸出可以看到,執(zhí)行順序的確是這樣子的。
First
Second
Third
Forth
IHostingStartup
與IStartupFilter
不同的是,IHostingStartup
可以在啟動時通過外部程序集向應用增加更多功能。不過這要求必須調用ConfigureWebHost
、ConfigureWebHostDefaults
等類似用來配置Web主機的擴展方法
我們經(jīng)常使用的Nuget包
SkyApm.Agent.AspNetCore
就使用了該特性。
下面我們就來看一下該如何使用它。
HostingStartup 程序集
要創(chuàng)建HostingStartup程序集,可以通過創(chuàng)建類庫項目或無入口點的控制臺應用來實現(xiàn)。
接下來咱們還是看一下上面提到過的SkyApm.Agent.AspNetCore
:
using SkyApm.Agent.AspNetCore; [assembly: HostingStartup(typeof(SkyApmHostingStartup))] namespace SkyApm.Agent.AspNetCore { internal class SkyApmHostingStartup : IHostingStartup { public void Configure(IWebHostBuilder builder) { builder.ConfigureServices(services => services.AddSkyAPM(ext => ext.AddAspNetCoreHosting())); } } }
該HostingStartup類:
- 實現(xiàn)了
IHostingStartup
接口 Configure
方法中使用IWebHostBuilder
來添加增強功能- 配置了
HostingStartup
特性
HostingStartup 特性
HostingStartup
特性用于標識哪個類是HostingStartup類,HostingStartup類需要實現(xiàn)IHostingStartup
接口。
當程序啟動時,會自動掃描入口程序集和配置的待激活的的程序集列表(參見下方:激活HostingStarup程序集),來找到所有的HostingStartup
特性,并通過反射的方式創(chuàng)建Startup
并調用Configure
方法。
using SkyApm.Agent.AspNetCore; [assembly: HostingStartup(typeof(SkyApmHostingStartup))] namespace SkyApm.Agent.AspNetCore { internal class SkyApmHostingStartup : IHostingStartup { public void Configure(IWebHostBuilder builder) { builder.ConfigureServices(services => services.AddSkyAPM(ext => ext.AddAspNetCoreHosting())); } } }
激活HostingStarup程序集
要激活HostingStarup程序集,我們有兩種配置方式:
1.使用環(huán)境變量(推薦)
使用環(huán)境變量,無需侵入程序代碼,所以我更推薦大家使用這種方式。
配置環(huán)境變量ASPNETCORE_HOSTINGSTARTUPASSEMBLIES
,多個程序集使用分號(;)進行分隔,用于添加要激活的程序集。變量WebHostDefaults.HostingStartupAssembliesKey
就是指代這個環(huán)境變量的Key。
另外,還有一個環(huán)境變量,叫做ASPNETCORE_HOSTINGSTARTUPEXCLUDEASSEMBLIES
,多個程序集使用分號(;)進行分隔,用于排除要激活的程序集。變量WebHostDefaults.HostingStartupExcludeAssembliesKey
就是指代這個環(huán)境變量的Key。
我們在 launchSettings.json 中添加兩個程序集:
"environmentVariables": { "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "SkyAPM.Agent.AspNetCore;HostingStartupLibrary" }
2.在程序中配置
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseSetting( WebHostDefaults.HostingStartupAssembliesKey, "SkyAPM.Agent.AspNetCore;HostingStartupLibrary") .UseStartup<Startup>(); });
這樣就配置完成了,很🐮🍺的一個功能點吧!
需要注意的是,無論使用哪種配置方式,當存在多個HostingStartup程序集時,將按配置這些程序集時的書寫順序執(zhí)行 Configure
方法。
多環(huán)境配置
一款軟件,一般要經(jīng)過需求分析、設計編碼,單元測試、集成測試以及系統(tǒng)測試等一系列測試流程,驗收,最終上線。那么,就至少需要4套環(huán)境來保證系統(tǒng)運行:
Development
:開發(fā)環(huán)境,用于開發(fā)人員在本地對應用進行調試運行Test
:測試環(huán)境,用于測試人員對應用進行測試Staging
:預發(fā)布環(huán)境,用于在正式上線之前,對應用進行集成、測試和預覽,或用于驗收Production
:生產環(huán)境,應用的正式線上環(huán)境
環(huán)境配置方式
通過環(huán)境變量ASPNETCORE_ENVIRONMENT
指定運行環(huán)境
注意:如果未指定環(huán)境,默認情況下,為 Production
在項目的Properties文件夾里面,有一個“l(fā)aunchSettings.json”文件,該文件是用于配置VS中項目啟動的。
接下來我們就在launchSettings.json
中配置一下。
先解釋一下該文件中出現(xiàn)的幾個參數(shù):
commandName
:指定要啟動的Web服務器,有三個可選值:
Project:啟動 Kestrel
IISExpress:啟動IIS Express
IIS:不啟用任何Web服務器,使用IIS
dotnetRunMessages
:bool字符串,指示當使用 dotnet run 命令時,終端能夠及時響應并輸出消息,具體參考stackoverflow和github issuelaunchBrowser
:bool值,指示當程序啟動后,是否打開瀏覽器launchUrl
:默認啟動路徑applicationUrl
:應用程序Url列表,多個URL之間使用分號(;
)進行分隔。當launchBrowser為true時,將{applicationUrl}/{launchUrl}作為瀏覽器默認訪問的UrlenvironmentVariables
:環(huán)境變量集合,在該集合內配置環(huán)境變量
{ "$schema": "http://json.schemastore.org/launchsettings.json", "profiles": { // 如果不指定profile,則默認選擇第一個 // Development "ASP.NET.WebAPI": { "commandName": "Project", "dotnetRunMessages": "true", "launchBrowser": true, "launchUrl": "weatherforecast", "applicationUrl": "http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, // Test "ASP.NET.WebAPI.Test": { "commandName": "Project", "dotnetRunMessages": "true", "launchBrowser": true, "launchUrl": "weatherforecast", "applicationUrl": "http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Test" } }, // Staging "ASP.NET.WebAPI.Staging": { "commandName": "Project", "dotnetRunMessages": "true", "launchBrowser": true, "launchUrl": "weatherforecast", "applicationUrl": "http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Staging" } }, // Production "ASP.NET.WebAPI.Production": { "commandName": "Project", "dotnetRunMessages": "true", "launchBrowser": true, "launchUrl": "weatherforecast", "applicationUrl": "http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Production" } }, // 用于測試在未指定環(huán)境時,默認是否為Production "ASP.NET.WebAPI.Default": { "commandName": "Project", "dotnetRunMessages": "true", "launchBrowser": true, "launchUrl": "weatherforecast", "applicationUrl": "http://localhost:5000" } } }
配置完成后,就可以在VS上方工具欄中的項目啟動處選擇啟動項了
基于環(huán)境的 Startup
Startup類支持針對不同環(huán)境進行個性化配置,有三種方式:
- 1.將
IWebHostEnvironment
注入 Startup 類 - 2.Startup 方法約定
- 3.Startup 類約定
1.將IWebHostEnvironment
注入 Startup 類
通過將IWebHostEnvironment
注入 Startup 類,然后在方法中使用條件判斷書寫不同環(huán)境下的代碼。該方式適用于多環(huán)境下,代碼差異較少的情況。
public class Startup { public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment) { Configuration = configuration; WebHostEnvironment = webHostEnvironment; } public IConfiguration Configuration { get; } public IWebHostEnvironment WebHostEnvironment { get; } public void ConfigureServices(IServiceCollection services) { if (WebHostEnvironment.IsDevelopment()) { Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}"); } else if (WebHostEnvironment.IsTest()) { Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}"); } else if (WebHostEnvironment.IsStaging()) { Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}"); } else if (WebHostEnvironment.IsProduction()) { Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}"); } } public void Configure(IApplicationBuilder app) { if (WebHostEnvironment.IsDevelopment()) { Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}"); } else if (WebHostEnvironment.IsTest()) { Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}"); } else if (WebHostEnvironment.IsStaging()) { Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}"); } else if (WebHostEnvironment.IsProduction()) { Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}"); } } } public static class AppHostEnvironmentEnvExtensions { public static bool IsTest(this IHostEnvironment hostEnvironment) { if (hostEnvironment == null) { throw new ArgumentNullException(nameof(hostEnvironment)); } return hostEnvironment.IsEnvironment(AppEnvironments.Test); } } public static class AppEnvironments { public static readonly string Test = nameof(Test); }
2.Startup 方法約定
上面的方式把不同環(huán)境的代碼放在了同一個方法中,看起來比較混亂也不容易區(qū)分。因此我們希望ConfigureServices
和Configure
能夠根據(jù)不同的環(huán)境進行代碼拆分。
我們可以通過方法命名約定來解決,約定Configure{EnvironmentName}Services
和Configure{EnvironmentName}Services
來裝載不同環(huán)境的代碼。如果當前環(huán)境沒有對應的方法,則使用原來的ConfigureServices
和Configure
方法。
我就只拿 Development 和 Production 舉例了
public class Startup { // 我這里注入 IWebHostEnvironment,僅僅是為了打印出來當前環(huán)境信息 public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment) { Configuration = configuration; WebHostEnvironment = webHostEnvironment; } public IConfiguration Configuration { get; } public IWebHostEnvironment WebHostEnvironment { get; } #region ConfigureServices private void StartupConfigureServices(IServiceCollection services) { Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}"); } public void ConfigureDevelopmentServices(IServiceCollection services) { StartupConfigureServices(services); } public void ConfigureProductionServices(IServiceCollection services) { StartupConfigureServices(services); } public void ConfigureServices(IServiceCollection services) { StartupConfigureServices(services); } #endregion #region Configure private void StartupConfigure(IApplicationBuilder app) { Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}"); } public void ConfigureDevelopment(IApplicationBuilder app) { StartupConfigure(app); } public void ConfigureProduction(IApplicationBuilder app) { StartupConfigure(app); } public void Configure(IApplicationBuilder app) { StartupConfigure(app); } #endregion }
3.Startup 類約定
該方式適用于多環(huán)境下,代碼差異較大的情況。
程序啟動時,會優(yōu)先尋找當前環(huán)境命名符合Startup{EnvironmentName}
的 Startup 類,如果找不到,則使用名稱為Startup
的類
首先,CreateHostBuilder
方法需要做一處修改
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { //webBuilder.UseStartup<Startup>(); webBuilder.UseStartup(typeof(Startup).GetTypeInfo().Assembly.FullName); });
接下來,就是為各個環(huán)境定義 Startup 類了(我就只拿 Development 和 Production 舉例了)
public class StartupDevelopment { // 我這里注入 IWebHostEnvironment,僅僅是為了打印出來當前環(huán)境信息 public StartupDevelopment(IConfiguration configuration, IWebHostEnvironment webHostEnvironment) { Configuration = configuration; WebHostEnvironment = webHostEnvironment; } public IConfiguration Configuration { get; } public IWebHostEnvironment WebHostEnvironment { get; } public void ConfigureServices(IServiceCollection services) { Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}"); } public void Configure(IApplicationBuilder app) { Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}"); } } public class StartupProduction { public StartupProduction(IConfiguration configuration, IWebHostEnvironment webHostEnvironment) { Configuration = configuration; WebHostEnvironment = webHostEnvironment; } public IConfiguration Configuration { get; } public IWebHostEnvironment WebHostEnvironment { get; } public void ConfigureServices(IServiceCollection services) { Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}"); } public void Configure(IApplicationBuilder app) { Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}"); } }
到此這篇關于理解ASP.NET Core 啟動類(Startup)的文章就介紹到這了,更多相關ASP.NET Core Startup內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
c# Random快速連續(xù)產生相同隨機數(shù)的解決方案
在寫數(shù)獨基類的時候為了產生隨機數(shù)的時候遇到奇怪的問題2009-03-03asp.net(c#)復數(shù)類(復數(shù)加減乘除四則運算)
asp.net(c#)復數(shù)類(復數(shù)加減乘除四則運算)...2007-06-06vs2012創(chuàng)建的ado.net模型無法實例化的解決方案
本文給大家分享的是升級vs2012后,發(fā)現(xiàn)創(chuàng)建數(shù)據(jù)模型無法實例化使用,嘗試了很多種方法,最后在度娘的幫助下,才解決了這個問題,這里記錄下來,分享給大家。2015-03-03ASp.net 文本框(TextBox)計算,判斷輸入的是否是數(shù)字
ASp.net文本計算,文本框數(shù)字輸入檢測,文本框的TextChanged事件,同時在屬性的Auto Post Back設置為True2009-07-07asp.net richTextBox中高亮顯示選中字符串或文本
最近開發(fā)程序需要對一段文本中的某個字符串進行高亮顯示,網(wǎng)上找了下資料2011-11-11ASP.NET Core Zero使用Power Tool工具
這篇文章介紹了ASP.NET Core Zero使用Power Tool工具的方法,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-02-02Win 2000下ASP.NET開發(fā)環(huán)境的配置
Win 2000在默認情況下是不支持ASP.NET的。必須對它進行一個環(huán)境的配置,本文將圖文介紹,在配置過程中遇到困難的朋友可以參考下2012-11-11C#和asp.net中鏈接數(shù)據(jù)庫中參數(shù)的幾種傳遞方法實例代碼
這篇文章介紹了C#和asp.net中鏈接數(shù)據(jù)庫中參數(shù)的幾種傳遞方法實例代碼,有需要的朋友可以參考一下2013-10-10