時(shí)間:2023-05-03 04:45:01 | 來(lái)源:網(wǎng)站運(yùn)營(yíng)
時(shí)間:2023-05-03 04:45:01 來(lái)源:網(wǎng)站運(yùn)營(yíng)
深入ASP.NET Core源代碼之 - HOST:測(cè)試代碼運(yùn)行環(huán)境:
OS:Ubuntu 18.04 64bit
.NET Core: 3.1.101
源代碼版本:release/3.1
dotenet new web -n SampleWeb指令會(huì)新建一個(gè)全新的 http://ASP.NET Core模板應(yīng)用程序,文件結(jié)構(gòu)如下:
SampleWeb| appsettings.Development.json| appsettings.json|---obj| Program.cs| Properties| SampleWeb.csproj| Startup.cs
Program.cs包含了配置及啟動(dòng) http://ASP.NET Core 應(yīng)用程序的全部代碼,一共只有寥寥幾行,非常簡(jiǎn)潔高效,我們今天的主角IWebHostBuilder、IWebHost就在其中。http://ASP.NET CORE 3.0 將早期版本的WebHostBuilder替換成了全新的基于GenericHost的HostBuilder,可以為 http://ASP.NET Core 應(yīng)用程序與非WEB應(yīng)用程序更好的集成,共享更多基礎(chǔ)代碼。具體信息可前往What's new in ASP.NET Core 3.0查看。
//...省略部分代碼namespace SmapleWeb{ 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>(); }); }}
Host.CreateDefaultBuilder()方法會(huì)創(chuàng)建一個(gè)默認(rèn)的HostBuilder對(duì)象,配置Startup類(lèi)作為HOST啟動(dòng)后的應(yīng)用程序配置入口點(diǎn)。IHostBuilder
接口:namespace Microsoft.Extensions.Hosting{ public interface IHostBuilder { IDictionary<object, object> Properties { get; } IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate); IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate); IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate); IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory); IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory); IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate); IHost Build(); }}
IHostBuilder 定義在dotnet/extensions項(xiàng)目中(GITHUB REPO)。 項(xiàng)目名稱(chēng)為:Microsoft.Extensions.Hosting.Abstractions。 名稱(chēng)空間Microsoft.Extensions.Hosting 源代碼IHostBuilder一共定義了八個(gè)接口成員,下表展示了這些接口成員的主要用途:
IHostBuilder
的默認(rèn)實(shí)現(xiàn):HostBuilder
HostBuilder 定義在dotnet/extensions項(xiàng)目中(GITHUB REPO)。 項(xiàng)目名稱(chēng)為:Microsoft.Extensions.Hosting。 名稱(chēng)空間Microsoft.Extensions.Hosting 源代碼在HostBuilder的默認(rèn)實(shí)現(xiàn)中,定義了一系列對(duì)應(yīng)的
List<Action>
對(duì)象來(lái)存儲(chǔ)用戶注冊(cè)到ConfigureHostConfiguration
、ConfigureAppConfiguration
、ConfigureServices
等方法中的配置函數(shù)private List<Action<IConfigurationBuilder>> _configureHostConfigActions = new List<Action<IConfigurationBuilder>>();private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions = new List<Action<HostBuilderContext, IConfigurationBuilder>>();private List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions = new List<Action<HostBuilderContext, IServiceCollection>>();
下面我們看一下Build方法的實(shí)現(xiàn);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>();}
Build方法的內(nèi)容也很簡(jiǎn)單,只有一個(gè)基本檢查和五個(gè)步驟。 1、首先檢查是否重復(fù)創(chuàng)建了IHost對(duì)象,每個(gè)IHostBuilder對(duì)象只能運(yùn)行一次Build方法,否則拋出異常,確保我們不會(huì)重復(fù)創(chuàng)建IHost對(duì)象。 2、第二步初始化創(chuàng)建IHost對(duì)象過(guò)程中需要使用到的基本配置信息。 3、第三步初始化應(yīng)用程序的Microsoft.Extensions.Hosting.IHostEnvironment
對(duì)象,用來(lái)向應(yīng)用程序提供程序運(yùn)行環(huán)境信息(Development、Stage、Production等)。 4、第四步初始化(根據(jù)第二部初始化的基本配置信息)創(chuàng)建IHost對(duì)象所需要的所有上下文對(duì)象。 5、最后創(chuàng)建IOC容器,并將默認(rèn)的IHost對(duì)象(Microsoft.Extensions.Hosting.Internal.Host
)注入到IOC容易中,然后將IHost對(duì)象從IOC容器里取出并返回給方法的調(diào)用方。IHostBuilder
就完成了它的全部使命。IHost
接口public interface IHost : IDisposable{ IServiceProvider Services { get; } Task StartAsync(CancellationToken cancellationToken = default); Task StopAsync(CancellationToken cancellationToken = default);}
IHost 定義在dotnet/extensions項(xiàng)目中(GITHUB REPO)。 項(xiàng)目名稱(chēng)為:Microsoft.Extensions.Hosting.Abstractions。 名稱(chēng)空間Microsoft.Extensions.Hosting 源代碼相較于
IHostBuilder
接口,IHost
接口的定義要簡(jiǎn)單的多,只有三個(gè)成員對(duì)象:IHost
的默認(rèn)實(shí)現(xiàn):Internal.Host
HostBuilder 定義在dotnet/extensions項(xiàng)目中(GITHUB REPO)。 項(xiàng)目名稱(chēng)為:Microsoft.Extensions.Hosting。 名稱(chēng)空間Microsoft.Extensions.Hosting.Internal 源代碼
StartAsync
方法的實(shí)現(xiàn):public async Task StartAsync(CancellationToken cancellationToken = default){ _logger.Starting(); using var combinedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _applicationLifetime.ApplicationStopping); var combinedCancellationToken = combinedCancellationTokenSource.Token; await _hostLifetime.WaitForStartAsync(combinedCancellationToken); combinedCancellationToken.ThrowIfCancellationRequested(); _hostedServices = Services.GetService<IEnumerable<IHostedService>>(); foreach (var hostedService in _hostedServices) { // Fire IHostedService.Start await hostedService.StartAsync(combinedCancellationToken).ConfigureAwait(false); } // Fire IHostApplicationLifetime.Started _applicationLifetime?.NotifyStarted(); _logger.Started();}
StartAsync
方法將注冊(cè)到HostBuilder上下文中的IHostedService
對(duì)象按照注冊(cè)時(shí)的先后順序執(zhí)行IHostedService.StartAsync()
方法進(jìn)行啟動(dòng)。 將所有的IHostedService
對(duì)象執(zhí)行啟動(dòng)完畢后,通過(guò)應(yīng)用程序生命周期管理對(duì)象IApplicationLifetime
通知應(yīng)用程序,啟動(dòng)就完成了。StopAsync
方法的實(shí)現(xiàn):public async Task StopAsync(CancellationToken cancellationToken = default){ _logger.Stopping(); using (var cts = new CancellationTokenSource(_options.ShutdownTimeout)) using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancellationToken)) { var token = linkedCts.Token; // Trigger IHostApplicationLifetime.ApplicationStopping _applicationLifetime?.StopApplication(); IList<Exception> exceptions = new List<Exception>(); if (_hostedServices != null) // Started? { foreach (var hostedService in _hostedServices.Reverse()) { token.ThrowIfCancellationRequested(); try { await hostedService.StopAsync(token).ConfigureAwait(false); } catch (Exception ex) { exceptions.Add(ex); } } } token.ThrowIfCancellationRequested(); await _hostLifetime.StopAsync(token); // Fire IHostApplicationLifetime.Stopped _applicationLifetime?.NotifyStopped(); if (exceptions.Count > 0) { var ex = new AggregateException("One or more hosted services failed to stop.", exceptions); _logger.StoppedWithException(ex); throw ex; } } _logger.Stopped();}
StopAsync
方法與StartAsync
方法基本類(lèi)型,首先通過(guò)應(yīng)用程序生命周期管理對(duì)象IApplicationLifetime
通知應(yīng)用程序即將開(kāi)始停止服務(wù),然后一次調(diào)用IHostedService
對(duì)象停止服務(wù)的運(yùn)行,最后通知應(yīng)用程序已完成結(jié)束方法。IHostBuilder
及默認(rèn)的IHost
對(duì)象源代碼中我們可以發(fā)現(xiàn),IHostBuidler
及IHost
對(duì)象僅僅提供了創(chuàng)建現(xiàn)代化的應(yīng)用程序(基于依賴(lài)注入、控制反轉(zhuǎn)等編程思想)的最基礎(chǔ)支持,而僅靠默認(rèn)的實(shí)現(xiàn)我們還無(wú)法構(gòu)建一個(gè)現(xiàn)代WEB應(yīng)用程序,因此ASP.NET Core應(yīng)用程序框架在默認(rèn)的IHostBuilder
及IHost
對(duì)象之上,提供了擴(kuò)展方法,幫助我們構(gòu)建一個(gè)基本的現(xiàn)代WEB應(yīng)用程序。CreateDefaultBuilder 定義在dotnet/extensions項(xiàng)目中(GITHUB REPO)。 項(xiàng)目名稱(chēng)為:Microsoft.Extensions.Hosting。 名稱(chēng)空間Microsoft.Extensions.Hosting 源代碼
CreateDefaultBuilder
方法:public static IHostBuilder CreateDefaultBuilder(string[] args){ var builder = new HostBuilder(); builder.UseContentRoot(Directory.GetCurrentDirectory()); builder.ConfigureHostConfiguration(config => { config.AddEnvironmentVariables(prefix: "DOTNET_"); if (args != null) { config.AddCommandLine(args); } }); builder.ConfigureAppConfiguration((hostingContext, config) => { var env = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName)) { var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); if (appAssembly != null) { config.AddUserSecrets(appAssembly, optional: true); } } config.AddEnvironmentVariables(); if (args != null) { config.AddCommandLine(args); } }) .ConfigureLogging((hostingContext, logging) => { var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); // IMPORTANT: This needs to be added *before* configuration is loaded, this lets // the defaults be overridden by the configuration. if (isWindows) { // Default the EventLogLoggerProvider to warning or above logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning); } logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); logging.AddConsole(); logging.AddDebug(); logging.AddEventSourceLogger(); if (isWindows) { // Add the EventLogLoggerProvider on windows machines logging.AddEventLog(); } }) .UseDefaultServiceProvider((context, options) => { var isDevelopment = context.HostingEnvironment.IsDevelopment(); options.ValidateScopes = isDevelopment; options.ValidateOnBuild = isDevelopment; }); return builder;}
從源代碼中我們可以看到,CreateDefaultBuilder
方法配置了默認(rèn)的ContentRoot配置項(xiàng),用于指示應(yīng)用程序從哪里獲取靜態(tài)文件和WEB程序的根目錄。加載了DOTNET_
前綴的系統(tǒng)環(huán)境變量,并從appsettings.json
和appsettings.{env.EnvironmentName}.json
文件中加載應(yīng)用程序配置信息。配置日志組件,并添加了日志系統(tǒng)的輸出路徑,而且針對(duì)Windows操作系統(tǒng)額外添加了日志的攔截器和WindowsEvent組件的日志輸出路徑。CreateDefaultBuilder有一個(gè)較長(zhǎng)的調(diào)用鏈,最終實(shí)際對(duì)http://ASP.NET Core應(yīng)用程序進(jìn)行配置的方法是WebHost.ConfigureWebDefaults
方法WebHost.ConfigureWebDefaults
方法定義在dotnet/aspnetcore項(xiàng)目中(GITHUB REPO)。 項(xiàng)目名稱(chēng)為:Microsoft.AspNetCore 名稱(chēng)空間Microsoft.AspNetCore 源代碼
CreateDefaultBuilder
方法:internal static void ConfigureWebDefaults(IWebHostBuilder builder) { builder.ConfigureAppConfiguration((Action<WebHostBuilderContext, IConfigurationBuilder>) ((ctx, cb) => { if (!ctx.HostingEnvironment.IsDevelopment()) return; StaticWebAssetsLoader.UseStaticWebAssets(ctx.HostingEnvironment, ctx.Configuration); })); builder.UseKestrel((Action<WebHostBuilderContext, KestrelServerOptions>) ((builderContext, options) => options.Configure((IConfiguration) builderContext.Configuration.GetSection("Kestrel")))).ConfigureServices((Action<WebHostBuilderContext, IServiceCollection>) ((hostingContext, services) => { services.PostConfigure<HostFilteringOptions>((Action<HostFilteringOptions>) (options => { if (options.AllowedHosts != null && options.AllowedHosts.Count != 0) return; string str = hostingContext.Configuration["AllowedHosts"]; string[] strArray1; if (str == null) strArray1 = (string[]) null; else strArray1 = str.Split(new char[1]{ ';' }, StringSplitOptions.RemoveEmptyEntries); string[] strArray2 = strArray1; HostFilteringOptions filteringOptions = options; string[] strArray3; if (strArray2 == null || strArray2.Length == 0) strArray3 = new string[1]{ "*" }; else strArray3 = strArray2; filteringOptions.AllowedHosts = (IList<string>) strArray3; })); services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>((IOptionsChangeTokenSource<HostFilteringOptions>) new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration)); services.AddTransient<IStartupFilter, HostFilteringStartupFilter>(); if (string.Equals("true", hostingContext.Configuration["ForwardedHeaders_Enabled"], StringComparison.OrdinalIgnoreCase)) { services.Configure<ForwardedHeadersOptions>((Action<ForwardedHeadersOptions>) (options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.KnownNetworks.Clear(); options.KnownProxies.Clear(); })); services.AddTransient<IStartupFilter, ForwardedHeadersStartupFilter>(); } services.AddRouting(); })).UseIIS().UseIISIntegration(); }
從源代碼中我們可以看到,ConfigureWebDefaults
方法配置了Web Server 組件Kestrel
用來(lái)處理HTTP通信,加載了CORS基礎(chǔ)配置,配置了ForwardedHeaders(用于支持代理服務(wù)如Nginx等),添加了基礎(chǔ)的路由組件并配置了IIS的支持組件。 于是ASP.NET Core應(yīng)用程序此時(shí)已經(jīng)可以支持HTTP通信,并且無(wú)需額外配置就可以運(yùn)行在代理服務(wù)器之上。還可以根據(jù)請(qǐng)求的URI信息進(jìn)行路由分配,查找對(duì)應(yīng)的Controller類(lèi)/Action方法。UserStartup
有一個(gè)較長(zhǎng)的調(diào)用鏈,最終實(shí)際對(duì)ASP.NET Core應(yīng)用程序進(jìn)行配置的方法是WebHostBuilderExtensions.UserStartup
方法WebHostBuilderExtensions.UserStartup
方法定義在dotnet/aspnetcore項(xiàng)目中(GITHUB REPO)。 項(xiàng)目名稱(chēng)為:Microsoft.AspNetCore.Hosting 名稱(chēng)空間Microsoft.AspNetCore.Hosting 源代碼
UserStartup
方法public static IWebHostBuilder UseStartup( this IWebHostBuilder hostBuilder, Type startupType) { string name = startupType.GetTypeInfo().Assembly.GetName().Name; hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, name); return hostBuilder is ISupportsStartup supportsStartup ? supportsStartup.UseStartup(startupType) : hostBuilder.ConfigureServices((Action<IServiceCollection>) (services => { if (typeof (IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo())) ServiceCollectionServiceExtensions.AddSingleton(services, typeof (IStartup), startupType); else ServiceCollectionServiceExtensions.AddSingleton(services, typeof (IStartup), (Func<IServiceProvider, object>) (sp => { IHostEnvironment requiredService = sp.GetRequiredService<IHostEnvironment>(); return (object) new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, requiredService.EnvironmentName)); })); })); }
UserStartup
方法將會(huì)在應(yīng)用程序中查找注冊(cè)的Startup
類(lèi),并執(zhí)行Startup
類(lèi)的配置方法對(duì)ASP.NET Core應(yīng)用程序執(zhí)行最后的,用戶層面的最終配置,在Startup
類(lèi)中,我們將詳細(xì)的配置我們的ASP.NET Core應(yīng)用程序已滿足我們對(duì)業(yè)務(wù)支撐的需要。這也是絕大多數(shù)開(kāi)發(fā)者首次接觸到的ASP.NET Core應(yīng)用程序配置的地點(diǎn)。 值得注意的是,在UserStartup
方法中,會(huì)首先查找聲明了IStartup
接口的Startup
類(lèi)(如果此處傳入的IWebHostBuiler
聲明了ISupportStartup
接口的話。)。因此除了將ASP.NET Core應(yīng)用程序直接編譯成可執(zhí)行程序外,我們也可以將ASP.NET Core應(yīng)用程序?qū)懺陬?lèi)庫(kù)類(lèi)型的項(xiàng)目中,由類(lèi)庫(kù)的使用者來(lái)幫助我們啟動(dòng)ASP.NET Core應(yīng)用程序。Startup
類(lèi):namespace SmapleWeb{ public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); }); } }}
空http://ASP.NET Core項(xiàng)目的Startup類(lèi)非常簡(jiǎn)單,實(shí)現(xiàn)了一個(gè)空的ConfigureServices
方法,在這個(gè)方法里,我們可以注入我們需要使用的各種服務(wù)類(lèi)或基礎(chǔ)組件類(lèi)。 并且實(shí)現(xiàn)了Configure
方法,注冊(cè)了基礎(chǔ)路由組件和終結(jié)點(diǎn)組件,能夠處理訪問(wèn)WEB ROOT的路徑的請(qǐng)求,并返回Hello World!關(guān)鍵詞:深入
客戶&案例
營(yíng)銷(xiāo)資訊
關(guān)于我們
客戶&案例
營(yíng)銷(xiāo)資訊
關(guān)于我們
微信公眾號(hào)
版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。