此外,在找寻各种方法的处理中可以看到环境变量的身影。所以用第二种方式可以依据不同的环境变量定义同类型但不同名称的方法,这样可以省去写不少if...else...的处理。
UseStartup方法中还只是申明了需要注册Startup类型,实际的调用是在WebHostBuilder类执行Build方法时发生的。
private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors) { ... foreach (var configureServices in _configureServicesDelegates) { configureServices(_context, services); } return services; }至于Startup类型中方法的调用时机,则需跟踪到WebHost类中。
首先是它的Initialize方法会确保获得Startup类的实例,并调用ConfigureServices方法注册服务接口。
private void EnsureApplicationServices() { if (_applicationServices == null) { EnsureStartup(); _applicationServices = _startup.ConfigureServices(_applicationServiceCollection); } } private void EnsureStartup() { if (_startup != null) { return; } _startup = _hostingServiceProvider.GetService<IStartup>(); ... }然后WebHost实例被启动时,它的BuildApplication方法会创建一个ApplicationBuilder实例,以其作为Configure方法参数,同时调用Configure方法,这时Configure方法中对那些middleware的处理方式(即Func<RequestDelegate, RequestDelegate>方法)会被加入到ApplicationBuilder之中。
private RequestDelegate BuildApplication() { try { _applicationServicesException?.Throw(); EnsureServer(); var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>(); var builder = builderFactory.CreateBuilder(Server.Features); builder.ApplicationServices = _applicationServices; var startupFilters = _applicationServices.GetService<IEnumerable<IStartupFilter>>(); Action<IApplicationBuilder> configure = _startup.Configure; foreach (var filter in startupFilters.Reverse()) { configure = filter.Configure(configure); } configure(builder); return builder.Build(); } ... }ApplicationBuilder的Build方法将这些处理逻辑嵌套起来,最底层的是返回404未找到的处理逻辑。
public RequestDelegate Build() { RequestDelegate app = context => { context.Response.StatusCode = 404; return Task.CompletedTask; }; foreach (var component in _components.Reverse()) { app = component(app); } return app; }BuildApplication方法返回已嵌套的RequestDelegate委托方法,并在之后生成的HostingApplication实例中,将其传入它的构造方法。
public virtual async Task StartAsync(CancellationToken cancellationToken = default) { HostingEventSource.Log.HostStart(); _logger = _applicationServices.GetRequiredService<ILogger<WebHost>>(); _logger.Starting(); var application = BuildApplication(); _applicationLifetime = _applicationServices.GetRequiredService<IApplicationLifetime>() as ApplicationLifetime; _hostedServiceExecutor = _applicationServices.GetRequiredService<HostedServiceExecutor>(); var diagnosticSource = _applicationServices.GetRequiredService<DiagnosticListener>(); var httpContextFactory = _applicationServices.GetRequiredService<IHttpContextFactory>(); var hostingApp = new HostingApplication(application, _logger, diagnosticSource, httpContextFactory); await Server.StartAsync(hostingApp, cancellationToken).ConfigureAwait(false); ... }上述方法中HostingApplication实例最终被传入KestrelServer的启动方法中,这样才能在其内部调用HostingApplication的ProcessRequestAsync方法,并开始层层调用诸多的RequestDelegate方法。
public Task ProcessRequestAsync(Context context) { return _application(context.HttpContext); }如果觉得使用Startup类还是有点麻烦的话,直接使用WebHostBuilder所提供的扩展方法也是同样的效果。
public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { HostingEnvironment = hostingContext.HostingEnvironment; Configuration = config.Build(); }) .ConfigureServices(services => { services.AddMvc(); }) .Configure(app => { if (HostingEnvironment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); } app.UseMvcWithDefaultRoute(); app.UseStaticFiles(); });