尊重作者劳动成果,转载请注明出处,谢谢!
目录
1.Web 主机
1.1 两种主机系统
1.2 IWebHostBuilder 的复用
2.配置 Web 主机
2.1 加载应用配置
2.2 注册依赖服务
2.3 托管 Web 服务
2.4 Startup 启动类
3.实解析
3.1 设计模型
3.2 GenericWebHostBuilder
3.3 GenericWebHostService
1.Web 主机
1.1 两种主机系统
上一节我们介绍了通用主机,这一节我们将介绍 Web 主机系统,我们将托管 Web 服务(用于监听、接收、处理并响应 HTTP 请求的服务)的主机称为 Web 主机。在 ASP.NET Core 2.x 时,我们使用的是基于 IWebHostBuilder/IWebHost 的主机系统,IWebHost 代表的是一个 Web 主机,同通用主机系统一样,一个 Web 主机也是表示对应用资源的封装,其中包括了相关的配置和依赖服务等,特别的是 Web 主机内置了一个 Web 服务。IWebHostBuilder 用于创建 IWebHost 对象,前者默认的实现类是 WebHostBuilder,前者的默认实现类是 WebHost(Web 服务被内置在 WebHost 中)。到了 ASP.NET Core 3.x 时,默认使用的不再是基于 IWebHostBuilder/IWebHost 的主机系统(即不使用 IWebHostBuilder 来创建 WebHost,也不使用 WebHost 来托管 Web 服务),而是基于 IHostBuilder/IHost 的通用主机系统。在通用主机系统中,Web 服务仅仅是作为一个被托管的服务(被定义为实现 IHostedService 接口的 GenericWebHostService 类)运行在主机上,通过这种设计方式,我们可以基于相关业务需求创建自己的托管服务并寄宿在主机之上。当然基于 IWebHostBuilder/IWebHost 的主机也提供了相应的方式托管我们的自定义服务(WebHost 内部注册的 HostedServiceExecutor 类会将注册的自定义托管服务提取出来并执行)。以下是两种主机系统托管 Web 服务的示意图:
其中 Web 服务是由 IServer 接口和 IHttpApplication 接口表示的,ASP.NET Core 的请求处理管道也可以视为这两个接口的组合。
1.2 IWebHostBuilder 的复用
为了紧跟 ASP.NET Core 的发展趋势,本章的内容我们不会介绍基于 IWebHostBuilder/WebHost 的主机系统(被视为“过时”的)。为了在新的通用主机上托管 Web 服务,就必须通过 IHostBuilder 接口注册一系列依赖的配置及服务,这样会导致大量原有的基于 IWebHostBuilder/IWebHost 的 API 需要重构。为了让工作变得简单,我们可以复用在 ASP.NET Core 2.x 上实现的一系列基于 IWebHostBuilder 接口的功能,实际上为了在通用主机上重用 IWebHostBuilder 接口,ASP.NET Core 3.x 提供了一个 GenericWebHostBuilder 类作为适配器,通过该适配器,我们可以使用 IWebHostBuilder 接口在主机(IHost)上加载配置、注册依赖服务以及配置依赖注入框架,即通过 IWebHostBuilder 接口添加的配置项和依赖服务最终都会被合并到主机之中。下面是 GenericWebHostBuilder 类的定义,部分代码如下所示,我们将在实现解析部分再详细对它进行介绍。
internal class GenericWebHostBuilder : IWebHostBuilder, ISupportsStartup, ISupportsUseDefaultServiceProvider { private readonly IHostBuilder _builder; private readonly IConfiguration _config; public GenericWebHostBuilder(IHostBuilder builder) { _builder = builder; _config = new ConfigurationBuilder() .AddEnvironmentVariables(prefix: "ASPNETCORE_") .Build(); ... } }
2.配置 Web 主机
前面我们讲了通过复用 IWebHostBuilder 接口,可以对 Web 主机加载配置和注册依赖服务,IWebHosBuilder 接口的定义如下:
public interface IWebHostBuilder { IWebHost Build(); IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate); IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices); IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices); string GetSetting(string key); IWebHostBuilder UseSetting(string key, string value); }
其中 WebHostBuilderContext 与通用主机中的 HostBuilderContext 具有类似的定义,它表示在创建 Web 主机过程中提供的上下文信息,其中包括了 Web 主机环境对象,主机的配置对象,使用该上下文信息可以针对不同的运行环境进行服务(依赖注入服务)的注册等。WebHostBuilderContext 的定义如下:
public class WebHostBuilderContext { public IWebHostEnvironment HostingEnvironment { get; set; } public IConfiguration Configuration { get; set; } }
IWebHostEnvironment 表示 Web 主机环境,它实现了 IHostEnvironment 接口(通用主机环境),定义如下:
public interface IWebHostEnvironment : IHostEnvironment { string WebRootPath { get; set; } IFileProvider WebRootFileProvider { get; set; } }
其中 WebRootPath 表示包含 Web 服务应用程序内容文件目录的绝对路径,而 WebRootFileProvider 表示该路径的 IFileProvider 对象。
2.1 加载应用配置
- ConfigureAppConfiguration: 该方法用于加载应用配置,其参数是一个 Action<WebHostBuilderContext, IConfigurationBuilder> 委托类型,即我们可以根据 WebHostBuilderContext 上下文信息,在不同的环境或配置下加载不同的配置源。
- UseSetting: 该方法用于在应用配置中添加或替换设置对应 Key 值的配置项。对应的 GetSetting 方法用于获取对应 Key 值的配置项。
IWebHostBuilder 接口提供了相应的扩展方法来设置主机环境的名称、 Web 服务的监听地址和 Web 服务应用程序内容文件的目录路径等,这些扩展方法调用了 UseSetting 方法来设置对应的配置项,如下代码所示:
public static class HostingAbstractionsWebHostBuilderExtensions { private static readonly string ServerUrlsSeparator = ";"; public static IWebHostBuilder UseConfiguration(this IWebHostBuilder hostBuilder, IConfiguration configuration) { foreach (var setting in configuration.AsEnumerable(makePathsRelative: true)) { hostBuilder.UseSetting(setting.Key, setting.Value); } return hostBuilder; } public static IWebHostBuilder UseEnvironment(this IWebHostBuilder hostBuilder, string environment) { if (environment == null) { throw new ArgumentNullException(nameof(environment)); } return hostBuilder.UseSetting(WebHostDefaults.EnvironmentKey, environment); } public static IWebHostBuilder UseContentRoot(this IWebHostBuilder hostBuilder, string contentRoot) { if (contentRoot == null) { throw new ArgumentNullException(nameof(contentRoot)); } return hostBuilder.UseSetting(WebHostDefaults.ContentRootKey, contentRoot); } public static IWebHostBuilder UseWebRoot(this IWebHostBuilder hostBuilder, string webRoot) { if (webRoot == null) { throw new ArgumentNullException(nameof(webRoot)); } return hostBuilder.UseSetting(WebHostDefaults.WebRootKey, webRoot); } public static IWebHostBuilder UseUrls(this IWebHostBuilder hostBuilder, params string[] urls) { if (urls == null) { throw new ArgumentNullException(nameof(urls)); } return hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, string.Join(ServerUrlsSeparator, urls)); } } public static class WebHostDefaults { public static readonly string ApplicationKey = "applicationName"; public static readonly string StartupAssemblyKey = "startupAssembly"; public static readonly string HostingStartupAssembliesKey = "hostingStartupAssemblies"; public static readonly string HostingStartupExcludeAssembliesKey = "hostingStartupExcludeAssemblies"; public static readonly string DetailedErrorsKey = "detailedErrors"; public static readonly string EnvironmentKey = "environment"; public static readonly string WebRootKey = "webroot"; public static readonly string CaptureStartupErrorsKey = "captureStartupErrors"; public static readonly string ServerUrlsKey = "urls"; public static readonly string ContentRootKey = "contentRoot"; public static readonly string PreferHostingUrlsKey = "preferHostingUrls"; public static readonly string PreventHostingStartupKey = "preventHostingStartup"; public static readonly string SuppressStatusMessagesKey = "suppressStatusMessages"; public static readonly string ShutdownTimeoutKey = "shutdownTimeoutSeconds"; public static readonly string StaticWebAssetsKey = "staticWebAssets"; }
2.2 注册依赖服务
- ConfigureServices:该方法用于注册应用程序所需的依赖服务,作用同 IHostBuilder 的 ConfigureServices 方法。
IWebHostBuilder 接口提供了一些扩展方法来方便我们注册所需要的服务,例如用于在主机中注册 Web 服务器的扩展方法,如下代码所示:
public static class HostingAbstractionsWebHostBuilderExtensions { public static IWebHostBuilder UseServer(this IWebHostBuilder hostBuilder, IServer server) { if (server == null) { throw new ArgumentNullException(nameof(server)); } return hostBuilder.ConfigureServices(services => { // It would be nicer if this was transient but we need to pass in the // factory instance directly services.AddSingleton(server); }); } }
2.3 托管 Web 服务
ASP.NET Core 对请求的处理是通过请求处理管道来完成的,管道中可以包含多个中间件(后序章节会详细介绍请求管道),而对请求的监听、接收和响应是通过位于管道最前面的服务器来处理的。也就是说首先由服务器接收请求,然后将请求分发给后序的中间件进行处理,最后服务器将请求处理结果响应给请求发起端(如浏览器等)。在 ASP.NET Core 中,Web 服务器由 IServer 接口表示,定义如下:
public interface IServer : IDisposable { IFeatureCollection Features { get; } Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken); Task StopAsync(CancellationToken cancellationToken); }
IServer 接口中的 StartAsync 方法和 StopAsync 方法分别用于启动和关闭 Web 服务器。
IHttpApplication<TContext> 接口表示 Web 服务器 对 HTTP 请求的处理流程,即对 IServer 接口接收到的原始请求的处理,包括创建请求上下文,将请求上下文交由中间件处理,最后释放该上下文,都是通过 IHttpApplication<TContext> 接口来实现的。接口的定义如下:
public interface IHttpApplication<TContext> { TContext CreateContext(IFeatureCollection contextFeatures); Task ProcessRequestAsync(TContext context); void DisposeContext(TContext context, Exception exception); }
CreateContext 方法用于从原始的请求中创建出对应的请求上下文对象,接着由 ProcessRequestAsync 方法对该上下文进行处理,最后由 DisposeContext 方法释放该上下文信息。
到目前为止,我们只需要简单了解在 ASP.NET Core 中是如何接收和处理请求的,详细的请求处理流程我们将在后序的章节进行介绍。既然我们已经通过接口实现了接收请求的 Web 服务器和处理请求的 Web 应用程序,那么他们是如何被托管在主机之中的呢?在通用主机一节和前面的介绍中,我们知道所有被托管的服务都是实现 IHostedService 接口的。同样的,针对 Web 服务的托管是通过 GenericWebHostService 类来完成的,GenericWebHostService 包含了一个 IServer 对象,启动该托管服务时正是调用了 IServer 对象的 StartAsync 方法来启动对请求的监听和处理。如下所示是经过简化的 GenericWebHostService 类的定义:
internal class GenericWebHostService : IHostedService { public GenericWebHostService(IServer server) { ... Server = server; ... } public IServer Server { get; } public async Task StartAsync(CancellationToken cancellationToken) { ... await Server.StartAsync(httpApplication, cancellationToken); ... } public async Task StopAsync(CancellationToken cancellationToken) { ... await Server.StopAsync(cancellationToken); ... } }
通过将 GenericWebHostService 托管服务注册到主机上,即可完成对 ASP.NET Core 应用的托管。IHostBuilder 接口提供了用来注册 GenericWebHostService 服务的扩展方法,同时该方法也体现了对 IWebHostBuilder 接口的复用。
public static class GenericHostWebHostBuilderExtensions { public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action<IWebHostBuilder> configure) { var webhostBuilder = new GenericWebHostBuilder(builder); configure(webhostBuilder); builder.ConfigureServices((context, services) => services.AddHostedService<GenericWebHostService>()); return builder; } }
注意:通过 ConfigureWebHost 方法注册的 GenericWebHostService 服务的声明周期是 Singleton 模式。
2.4 Startup 启动类
除了可以通过使用 IHostBuilder 接口和 IWebHostBuilder 接口提供的方法加载应用配置和注册依赖服务外,我们还可以通过约定的 Startup 类型来添加应用的配置和注册应用所需要的服务,在实际开发过程中,我们更倾向于使用这种方式来对主机进行配置。Startup 类型一般的定义如下代码所示:
public class Startup { public void Configure(IApplicationBuilder app); public void ConfigureServices(IServiceCollection services); }
- Configure:该方法用于配置请求处理管理,即注册中间件。该方法是必须的。
- ConfigureServices:该方法用于注册依赖服务。该方法是可选的。
IWebHostBuilder 接口提供了相应的扩展方法来注册 Startup 类,如下代码所示:
public static class WebHostBuilderExtensions { public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType) { var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name; hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName); // Light up the GenericWebHostBuilder implementation if (hostBuilder is ISupportsStartup supportsStartup) { return supportsStartup.UseStartup(startupType); } return hostBuilder .ConfigureServices(services => { if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo())) { services.AddSingleton(typeof(IStartup), startupType); } else { services.AddSingleton(typeof(IStartup), sp => { var hostingEnvironment = sp.GetRequiredService<IHostEnvironment>(); return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName)); }); } }); } public static IWebHostBuilder UseStartup<TStartup>(this IWebHostBuilder hostBuilder) where TStartup : class { return hostBuilder.UseStartup(typeof(TStartup)); } }
如果在其它程序集中定义了 Startup 类,我们还可以通过调用 IWebHostBuilder 的 UseStartup(this IWebHostBuilder hostBuilder, string startupAssemblyName) 方法将该程序集设置为启动程序集,如下代码所示:
public static class HostingAbstractionsWebHostBuilderExtensions { public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, string startupAssemblyName) { if (startupAssemblyName == null) { throw new ArgumentNullException(nameof(startupAssemblyName)); } return hostBuilder .UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName) .UseSetting(WebHostDefaults.StartupAssemblyKey, startupAssemblyName); } }
3.实现解析
3.1 设计模型
Web 主机系统涉及的主要核心对象关系如下图所示:
3.2 GenericWebHostBuilder
GenericWebHostBuilder 是为了在通用主机系统中复用 IWebHostBuilder 接口而定义的另一个实现类,GenericWebHostBuilder 类实际上封装(适配)了 IHostBuilder 对象,针对配置的加载和服务的注册,都是通过调用 IHostBuilder 接口的方法来实现的。如下代码所示:
internal class GenericWebHostBuilder : IWebHostBuilder, ISupportsStartup, ISupportsUseDefaultServiceProvider { private readonly IHostBuilder _builder; public GenericWebHostBuilder(IHostBuilder builder) { _builder = builder; ... } public IWebHost Build() { throw new NotSupportedException($"Building this implementation of {nameof(IWebHostBuilder)} is not supported."); } private WebHostBuilderContext GetWebHostBuilderContext(HostBuilderContext context) { if (!context.Properties.TryGetValue(typeof(WebHostBuilderContext), out var contextVal)) { var options = new WebHostOptions(context.Configuration, Assembly.GetEntryAssembly()?.GetName().Name); var webHostBuilderContext = new WebHostBuilderContext { Configuration = context.Configuration, HostingEnvironment = new HostingEnvironment(), }; webHostBuilderContext.HostingEnvironment.Initialize(context.HostingEnvironment.ContentRootPath, options); context.Properties[typeof(WebHostBuilderContext)] = webHostBuilderContext; context.Properties[typeof(WebHostOptions)] = options; return webHostBuilderContext; } // Refresh config, it's periodically updated/replaced var webHostContext = (WebHostBuilderContext)contextVal; webHostContext.Configuration = context.Configuration; return webHostContext; } }
GetWebHostBuilderContext 私有方法用于根据 HostBuilderContext 对象创建并初始化 WebHostBuilderContext 对象。
注意:GenericWebHostBuilder 的 Build 方法不提供创建 IWebHost 对象的功能。
3.2.1 加载主机及应用配置
在通用主机系统中,主机及应用的配置是通过 ConfigureHostConfiguration 方法和 ConfigureAppConfiguration 方法来完成的。而 GenericWebHostBuilder 针对主机及应用的配置也是通过调用这两个方法来实现的,如下代码所示:
internal class GenericWebHostBuilder : IWebHostBuilder, ISupportsStartup, ISupportsUseDefaultServiceProvider { private readonly IHostBuilder _builder; private readonly IConfiguration _config; private HostingStartupWebHostBuilder _hostingStartupWebHostBuilder; public GenericWebHostBuilder(IHostBuilder builder) { _builder = builder; _config = new ConfigurationBuilder() .AddEnvironmentVariables(prefix: "ASPNETCORE_") .Build(); _builder.ConfigureHostConfiguration(config => { config.AddConfiguration(_config); }); ... } public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate) { _builder.ConfigureAppConfiguration((context, builder) => { var webhostBuilderContext = GetWebHostBuilderContext(context); configureDelegate(webhostBuilderContext, builder); }); return this; } public string GetSetting(string key) { return _config[key]; } public IWebHostBuilder UseSetting(string key, string value) { _config[key] = value; return this; } }
GenericWebHostBuilder 类的构造函数首先创建了一个 ConfigurationBuilder 对象,并加载了以“ASPNETCORE_”为前缀的环境变量,接着调用了 IHostBuilder 接口的 ConfigureHostConfiguration 方法将 ConfigurationBuilder 创建的 IConfiguration 对象添加到主机配置中。
ConfigureAppConfiguration 方法直接调用 IHostBuilder 接口的 ConfigureAppConfiguration 方法,将对应的配置项添加到应用配置中。
3.2.2 注册依赖服务
在通用主机系统中,依赖服务的注册是通过 ConfigureServices 方法来完成的。而 GenericWebHostBuilder 类针对依赖服务的注册也是通过调用该方法来实现的,如下代码所示:
internal class GenericWebHostBuilder : IWebHostBuilder, ISupportsStartup, ISupportsUseDefaultServiceProvider { private readonly IHostBuilder _builder; public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices) { return ConfigureServices((context, services) => configureServices(services)); } public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices) { _builder.ConfigureServices((context, builder) => { var webhostBuilderContext = GetWebHostBuilderContext(context); configureServices(webhostBuilderContext, builder); }); return this; } }
3.2.3 配置默认的依赖注入框架
GenericWebHostBuilder 实现了 ISupportsUseDefaultServiceProvider 接口,ISupportsUseDefaultServiceProvider 接口的定义如下:
internal interface ISupportsUseDefaultServiceProvider { IWebHostBuilder UseDefaultServiceProvider(Action<WebHostBuilderContext, ServiceProviderOptions> configure); }
UseDefaultServiceProvider 方法用于在 ASP.NET Core 中使用默认的依赖注入框架并对其进行相应的属性设置,实现代码如下所示:
internal class GenericWebHostBuilder : IWebHostBuilder, ISupportsStartup, ISupportsUseDefaultServiceProvider { private readonly IHostBuilder _builder; public GenericWebHostBuilder(IHostBuilder builder) { _builder = builder; ... } public IWebHostBuilder UseDefaultServiceProvider(Action<WebHostBuilderContext, ServiceProviderOptions> configure) { _builder.UseServiceProviderFactory(context => { var webHostBuilderContext = GetWebHostBuilderContext(context); var options = new ServiceProviderOptions(); configure(webHostBuilderContext, options); return new DefaultServiceProviderFactory(options); }); return this; } }
UseDefaultServiceProvider 方法将内置的 DefaultServiceProviderFactory 工厂对象作为参数直接调用 IHostBuilder 的 UseDefaultServiceProvider 方法。
3.3 GenericWebHostService
经过前面的介绍,GenericWebHostService 代表的是一个寄宿在主机上的 Web 服务,该服务包含了 ASP.NET Core 框架内置的 Web 服务器和中间件,以及由我们基于应用需求而创建的中间件。GenericWebHostService 的具体实现如下代码所示:
internal class GenericWebHostService : IHostedService { public GenericWebHostService(IOptions<GenericWebHostServiceOptions> options, IServer server, ILoggerFactory loggerFactory, DiagnosticListener diagnosticListener, IHttpContextFactory httpContextFactory, IApplicationBuilderFactory applicationBuilderFactory, IEnumerable<IStartupFilter> startupFilters, IConfiguration configuration, IWebHostEnvironment hostingEnvironment) { Options = options.Value; Server = server; Logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Hosting.Diagnostics"); LifetimeLogger = loggerFactory.CreateLogger("Microsoft.Hosting.Lifetime"); DiagnosticListener = diagnosticListener; HttpContextFactory = httpContextFactory; ApplicationBuilderFactory = applicationBuilderFactory; StartupFilters = startupFilters; Configuration = configuration; HostingEnvironment = hostingEnvironment; } public GenericWebHostServiceOptions Options { get; } public IServer Server { get; } public ILogger Logger { get; } // Only for high level lifetime events public ILogger LifetimeLogger { get; } public DiagnosticListener DiagnosticListener { get; } public IHttpContextFactory HttpContextFactory { get; } public IApplicationBuilderFactory ApplicationBuilderFactory { get; } public IEnumerable<IStartupFilter> StartupFilters { get; } public IConfiguration Configuration { get; } public IWebHostEnvironment HostingEnvironment { get; } public async Task StartAsync(CancellationToken cancellationToken) { HostingEventSource.Log.HostStart(); var serverAddressesFeature = Server.Features?.Get<IServerAddressesFeature>(); var addresses = serverAddressesFeature?.Addresses; if (addresses != null && !addresses.IsReadOnly && addresses.Count == 0) { var urls = Configuration[WebHostDefaults.ServerUrlsKey]; if (!string.IsNullOrEmpty(urls)) { serverAddressesFeature.PreferHostingUrls = WebHostUtilities.ParseBool(Configuration, WebHostDefaults.PreferHostingUrlsKey); foreach (var value in urls.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { addresses.Add(value); } } } RequestDelegate application = null; try { Action<IApplicationBuilder> configure = Options.ConfigureApplication;var builder = ApplicationBuilderFactory.CreateBuilder(Server.Features); foreach (var filter in StartupFilters.Reverse()) { configure = filter.Configure(configure); } configure(builder); // Build the request pipeline application = builder.Build(); } catch (Exception ex) { Logger.ApplicationError(ex); if (!Options.WebHostOptions.CaptureStartupErrors) { throw; } } var httpApplication = new HostingApplication(application, Logger, DiagnosticListener, HttpContextFactory); await Server.StartAsync(httpApplication, cancellationToken); if (addresses != null) { foreach (var address in addresses) { LifetimeLogger.LogInformation("Now listening on: {address}", address); } } } public async Task StopAsync(CancellationToken cancellationToken) { try { await Server.StopAsync(cancellationToken); } finally { HostingEventSource.Log.HostStop(); } } }
GenericWebHostService 构造函数会注入一系列依赖的服务,其中包括用来创建 HttpContext(请求上下文)的 IHttpContextFactory 对象、用来创建 IApplicationBuilder 对象的 IApplicationBuilderFactory 对象等。 StartAsync 方法将在主机启动(调用 IHost.StartAsync 或 IHost.Run 方法)是被调用,通过调用 StartAsync 方法,一个基于 ASP.NET Core 的应用正式被启动起来。由于在构造函数和 StartAsync 方法内部涉及的一些接口或对象还未介绍,所以这里只对该方法的启动过程做简要的描述,在后序讲解完请求管道的章节后,我们再回过头来对 StartAsync 方法做详细介绍。
StartAsync 方法的启动流程大致可分为以下 4 个步骤:
- 通过 IServerAddressesFeature 特性和 IWebHostBuilder 的 UseUrls 扩展方法来设置 Web 服务器的监听地址。
- 通过 IApplicationBuilderFactory 工厂对象创建 IApplicationBuilder 对象,接着利用IApplicationBuilder 对象将注册的所有中间件构建成一个请求管道(一个 RequestDelegate 对象)。
- 创建 HostingApplication 对象,HostingApplication 是 IHttpApplication<TContext> 接口的默认实现类,该接口可表示一个 Web 应用程序,用于创建请求上下文,处理请求上下文,并最终释放。
- 使用 HostingApplication 对象作为参数,调用 IServer 对象的 StartAsync 方法启动 Web 服务器。