先看下如下的扩展方法
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;
}
}
当我们调用IHostBuilder的扩展方法ConfigureWebHost时候,系统创建了GenericWebHostBuilder类,并注册GenericWebHostService类
再分析下GenericWebHostBuilder
public GenericWebHostBuilder(IHostBuilder builder)
{
_builder = builder;
_config = new ConfigurationBuilder()
.AddEnvironmentVariables(prefix: "ASPNETCORE_")
.Build();
_builder.ConfigureHostConfiguration(config =>
{
config.AddConfiguration(_config);
// We do this super early but still late enough that we can process the configuration
// wired up by calls to UseSetting
ExecuteHostingStartups();
});
// IHostingStartup needs to be executed before any direct methods on the builder
// so register these callbacks first
_builder.ConfigureAppConfiguration((context, configurationBuilder) =>
{
if (_hostingStartupWebHostBuilder != null)
{
var webhostContext = GetWebHostBuilderContext(context);
_hostingStartupWebHostBuilder.ConfigureAppConfiguration(webhostContext, configurationBuilder);
}
});
_builder.ConfigureServices((context, services) =>
{
var webhostContext = GetWebHostBuilderContext(context);
var webHostOptions = (WebHostOptions)context.Properties[typeof(WebHostOptions)];
// Add the IHostingEnvironment and IApplicationLifetime from Microsoft.AspNetCore.Hosting
services.AddSingleton(webhostContext.HostingEnvironment);
#pragma warning disable CS0618 // Type or member is obsolete
services.AddSingleton((AspNetCore.Hosting.IHostingEnvironment)webhostContext.HostingEnvironment);
services.AddSingleton<IApplicationLifetime, GenericWebHostApplicationLifetime>();
#pragma warning restore CS0618 // Type or member is obsolete
services.Configure<GenericWebHostServiceOptions>(options =>
{
// Set the options
options.WebHostOptions = webHostOptions;
// Store and forward any startup errors
options.HostingStartupExceptions = _hostingStartupErrors;
});
// REVIEW: This is bad since we don't own this type. Anybody could add one of these and it would mess things up
// We need to flow this differently
var listener = new DiagnosticListener("Microsoft.AspNetCore");
services.TryAddSingleton<DiagnosticListener>(listener);
services.TryAddSingleton<DiagnosticSource>(listener);
services.TryAddSingleton<IHttpContextFactory, DefaultHttpContextFactory>();
services.TryAddScoped<IMiddlewareFactory, MiddlewareFactory>();
services.TryAddSingleton<IApplicationBuilderFactory, ApplicationBuilderFactory>();
// IMPORTANT: This needs to run *before* direct calls on the builder (like UseStartup)
_hostingStartupWebHostBuilder?.ConfigureServices(webhostContext, services);
// Support UseStartup(assemblyName)
if (!string.IsNullOrEmpty(webHostOptions.StartupAssembly))
{
try
{
var startupType = StartupLoader.FindStartupType(webHostOptions.StartupAssembly, webhostContext.HostingEnvironment.EnvironmentName);
UseStartup(startupType, context, services);
}
catch (Exception ex) when (webHostOptions.CaptureStartupErrors)
{
var capture = ExceptionDispatchInfo.Capture(ex);
services.Configure<GenericWebHostServiceOptions>(options =>
{
options.ConfigureApplication = app =>
{
// Throw if there was any errors initializing startup
capture.Throw();
};
});
}
}
});
}
GenericWebHostBuilder的构造函数接收IHostBuilder作为参数,其实是把asp.netcore用到的服务和配置加载到IHostBuilder中
添加一“ASPNETCORE_”开头的环境变量
寻找程序集有HostingStartupAttribute的特性,调用IHostingStartup接口对IWebHostBuilder进行配置
services.TryAddSingleton<IHttpContextFactory, DefaultHttpContextFactory>();
services.TryAddScoped<IMiddlewareFactory, MiddlewareFactory>();
services.TryAddSingleton<IApplicationBuilderFactory, ApplicationBuilderFactory>();
接下来调用实现IStartUp接口的类
private void UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services)
{
var webHostBuilderContext = GetWebHostBuilderContext(context);
var webHostOptions = (WebHostOptions)context.Properties[typeof(WebHostOptions)];
ExceptionDispatchInfo startupError = null;
object instance = null;
ConfigureBuilder configureBuilder = null;
try
{
// We cannot support methods that return IServiceProvider as that is terminal and we need ConfigureServices to compose
if (typeof(IStartup).IsAssignableFrom(startupType))
{
throw new NotSupportedException($"{typeof(IStartup)} isn't supported");
}
if (StartupLoader.HasConfigureServicesIServiceProviderDelegate(startupType, context.HostingEnvironment.EnvironmentName))
{
throw new NotSupportedException($"ConfigureServices returning an {typeof(IServiceProvider)} isn't supported.");
}
instance = ActivatorUtilities.CreateInstance(new HostServiceProvider(webHostBuilderContext), startupType);
context.Properties[_startupKey] = instance;
// Startup.ConfigureServices
var configureServicesBuilder = StartupLoader.FindConfigureServicesDelegate(startupType, context.HostingEnvironment.EnvironmentName);
var configureServices = configureServicesBuilder.Build(instance);
configureServices(services);
// REVIEW: We're doing this in the callback so that we have access to the hosting environment
// Startup.ConfigureContainer
var configureContainerBuilder = StartupLoader.FindConfigureContainerDelegate(startupType, context.HostingEnvironment.EnvironmentName);
if (configureContainerBuilder.MethodInfo != null)
{
var containerType = configureContainerBuilder.GetContainerType();
// Store the builder in the property bag
_builder.Properties[typeof(ConfigureContainerBuilder)] = configureContainerBuilder;
var actionType = typeof(Action<,>).MakeGenericType(typeof(HostBuilderContext), containerType);
// Get the private ConfigureContainer method on this type then close over the container type
var configureCallback = GetType().GetMethod(nameof(ConfigureContainer), BindingFlags.NonPublic | BindingFlags.Instance)
.MakeGenericMethod(containerType)
.CreateDelegate(actionType, this);
// _builder.ConfigureContainer<T>(ConfigureContainer);
typeof(IHostBuilder).GetMethods().First(m => m.Name == nameof(IHostBuilder.ConfigureContainer))
.MakeGenericMethod(containerType)
.InvokeWithoutWrappingExceptions(_builder, new object[] { configureCallback });
}
// Resolve Configure after calling ConfigureServices and ConfigureContainer
configureBuilder = StartupLoader.FindConfigureDelegate(startupType, context.HostingEnvironment.EnvironmentName);
}
catch (Exception ex) when (webHostOptions.CaptureStartupErrors)
{
startupError = ExceptionDispatchInfo.Capture(ex);
}
// Startup.Configure
services.Configure<GenericWebHostServiceOptions>(options =>
{
options.ConfigureApplication = app =>
{
// Throw if there was any errors initializing startup
startupError?.Throw();
// Execute Startup.Configure
if (instance != null && configureBuilder != null)
{
configureBuilder.Build(instance)(app);
}
};
});
}
先找到ConfigureServices方法注册服务
再找到ConfigureContainer方法配置容器
最后找到Configure方法配置请求链
我们再看下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;
if (configure == null)
{
throw new InvalidOperationException($"No application configured. Please specify an application via IWebHostBuilder.UseStartup, IWebHostBuilder.Configure, or specifying the startup assembly via {nameof(WebHostDefaults.StartupAssemblyKey)} in the web host configuration.");
}
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;
}
application = BuildErrorPageApplication(ex);
}
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);
}
}
if (Logger.IsEnabled(LogLevel.Debug))
{
foreach (var assembly in Options.WebHostOptions.GetFinalHostingStartupAssemblies())
{
Logger.LogDebug("Loaded hosting startup assembly {assemblyName}", assembly);
}
}
if (Options.HostingStartupExceptions != null)
{
foreach (var exception in Options.HostingStartupExceptions.InnerExceptions)
{
Logger.HostingStartupAssemblyError(exception);
}
}
}
private RequestDelegate BuildErrorPageApplication(Exception exception)
{
if (exception is TargetInvocationException tae)
{
exception = tae.InnerException;
}
var showDetailedErrors = HostingEnvironment.IsDevelopment() || Options.WebHostOptions.DetailedErrors;
var model = new ErrorPageModel
{
RuntimeDisplayName = RuntimeInformation.FrameworkDescription
};
var systemRuntimeAssembly = typeof(System.ComponentModel.DefaultValueAttribute).GetTypeInfo().Assembly;
var assemblyVersion = new AssemblyName(systemRuntimeAssembly.FullName).Version.ToString();
var clrVersion = assemblyVersion;
model.RuntimeArchitecture = RuntimeInformation.ProcessArchitecture.ToString();
var currentAssembly = typeof(ErrorPage).GetTypeInfo().Assembly;
model.CurrentAssemblyVesion = currentAssembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
.InformationalVersion;
model.ClrVersion = clrVersion;
model.OperatingSystemDescription = RuntimeInformation.OSDescription;
model.ShowRuntimeDetails = showDetailedErrors;
if (showDetailedErrors)
{
var exceptionDetailProvider = new ExceptionDetailsProvider(
HostingEnvironment.ContentRootFileProvider,
sourceCodeLineCount: 6);
model.ErrorDetails = exceptionDetailProvider.GetDetails(exception);
}
else
{
model.ErrorDetails = new ExceptionDetails[0];
}
var errorPage = new ErrorPage(model);
return context =>
{
context.Response.StatusCode = 500;
context.Response.Headers[HeaderNames.CacheControl] = "no-cache";
context.Response.ContentType = "text/html; charset=utf-8";
return errorPage.ExecuteAsync(context);
};
}
public async Task StopAsync(CancellationToken cancellationToken)
{
try
{
await Server.StopAsync(cancellationToken);
}
finally
{
HostingEventSource.Log.HostStop();
}
}
}
看下核心方法StartAsync
从GenericWebHostServiceOptions的ConfigureApplication属性中获取RequestDelegate委托链
调用IApplicationBuilderFactory接口的CreateBuilder创建IApplicationBuilder
再调用IStartupFilter的Configure方法配置整个委托链
接着调用IApplicationBuilder方法构建最终的RequestDelegate
接着创建HostingApplication对象
最后调用IServer接口的StartAsync开始处理请求
来源:https://www.cnblogs.com/lanpingwang/p/12640734.html