| File: WebHostBuilder.cs | Web Access |
| Project: src\aspnetcore\src\Hosting\Hosting\src\Microsoft.AspNetCore.Hosting.csproj (Microsoft.AspNetCore.Hosting) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #nullable enable using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.ExceptionServices; using Microsoft.AspNetCore.Hosting.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Shared; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Hosting; /// <summary> /// A builder for <see cref="IWebHost"/> /// </summary> [Obsolete("WebHostBuilder is deprecated in favor of HostBuilder and WebApplicationBuilder. For more information, visit https://aka.ms/aspnet/deprecate/004.", DiagnosticId = "ASPDEPR004", UrlFormat = Obsoletions.AspNetCoreDeprecate004Url)] public class WebHostBuilder : IWebHostBuilder { private readonly HostingEnvironment _hostingEnvironment; private readonly IConfiguration _config; private readonly WebHostBuilderContext _context; private WebHostOptions? _options; private bool _webHostBuilt; private Action<WebHostBuilderContext, IServiceCollection>? _configureServices; private Action<WebHostBuilderContext, IConfigurationBuilder>? _configureAppConfigurationBuilder; /// <summary> /// Initializes a new instance of the <see cref="WebHostBuilder"/> class. /// </summary> public WebHostBuilder() { _hostingEnvironment = new HostingEnvironment(); _config = new ConfigurationBuilder() .AddEnvironmentVariables(prefix: "ASPNETCORE_") .Build(); if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.EnvironmentKey))) { // Try adding legacy environment keys, never remove these. UseSetting(WebHostDefaults.EnvironmentKey, Environment.GetEnvironmentVariable("Hosting:Environment") ?? Environment.GetEnvironmentVariable("ASPNET_ENV")); } if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.ServerUrlsKey))) { // Try adding legacy url key, never remove this. UseSetting(WebHostDefaults.ServerUrlsKey, Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS")); } _context = new WebHostBuilderContext { Configuration = _config }; } /// <summary> /// Get the setting value from the configuration. /// </summary> /// <param name="key">The key of the setting to look up.</param> /// <returns>The value the setting currently contains.</returns> public string? GetSetting(string key) { return _config[key]; } /// <summary> /// Add or replace a setting in the configuration. /// </summary> /// <param name="key">The key of the setting to add or replace.</param> /// <param name="value">The value of the setting to add or replace.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> public IWebHostBuilder UseSetting(string key, string? value) { _config[key] = value; return this; } /// <summary> /// Adds a delegate for configuring additional services for the host or web application. This may be called /// multiple times. /// </summary> /// <param name="configureServices">A delegate for configuring the <see cref="IServiceCollection"/>.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices) { ArgumentNullException.ThrowIfNull(configureServices); return ConfigureServices((_, services) => configureServices(services)); } /// <summary> /// Adds a delegate for configuring additional services for the host or web application. This may be called /// multiple times. /// </summary> /// <param name="configureServices">A delegate for configuring the <see cref="IServiceCollection"/>.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices) { _configureServices += configureServices; return this; } /// <summary> /// Adds a delegate for configuring the <see cref="IConfigurationBuilder"/> that will construct an <see cref="IConfiguration"/>. /// </summary> /// <param name="configureDelegate">The delegate for configuring the <see cref="IConfigurationBuilder" /> that will be used to construct an <see cref="IConfiguration" />.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> /// <remarks> /// The <see cref="IConfiguration"/> and <see cref="ILoggerFactory"/> on the <see cref="WebHostBuilderContext"/> are uninitialized at this stage. /// The <see cref="IConfigurationBuilder"/> is pre-populated with the settings of the <see cref="IWebHostBuilder"/>. /// </remarks> public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate) { _configureAppConfigurationBuilder += configureDelegate; return this; } /// <summary> /// Builds the required services and an <see cref="IWebHost"/> which hosts a web application. /// </summary> public IWebHost Build() { if (_webHostBuilt) { throw new InvalidOperationException(Resources.WebHostBuilder_SingleInstance); } _webHostBuilt = true; var hostingServices = BuildCommonServices(out var hostingStartupErrors); var applicationServices = hostingServices.Clone(); var hostingServiceProvider = GetProviderFromFactory(hostingServices); if (!_options.SuppressStatusMessages) { // Warn about deprecated environment variables if (Environment.GetEnvironmentVariable("Hosting:Environment") != null) { Console.WriteLine("The environment variable 'Hosting:Environment' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'"); } if (Environment.GetEnvironmentVariable("ASPNET_ENV") != null) { Console.WriteLine("The environment variable 'ASPNET_ENV' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'"); } if (Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS") != null) { Console.WriteLine("The environment variable 'ASPNETCORE_SERVER.URLS' is obsolete and has been replaced with 'ASPNETCORE_URLS'"); } } AddApplicationServices(applicationServices, hostingServiceProvider); var host = new WebHost( applicationServices, hostingServiceProvider, _options, _config, hostingStartupErrors); try { host.Initialize(); // resolve configuration explicitly once to mark it as resolved within the // service provider, ensuring it will be properly disposed with the provider _ = host.Services.GetService<IConfiguration>(); var logger = host.Services.GetRequiredService<ILogger<WebHost>>(); // Warn about duplicate HostingStartupAssemblies var assemblyNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies()) { if (!assemblyNames.Add(assemblyName) && logger.IsEnabled(LogLevel.Warning)) { logger.LogWarning($"The assembly {assemblyName} was specified multiple times. Hosting startup assemblies should only be specified once."); } } return host; } catch { // Dispose the host if there's a failure to initialize, this should dispose // services that were constructed until the exception was thrown host.Dispose(); throw; } static IServiceProvider GetProviderFromFactory(IServiceCollection collection) { var provider = collection.BuildServiceProvider(); var factory = provider.GetService<IServiceProviderFactory<IServiceCollection>>(); if (factory != null && factory is not DefaultServiceProviderFactory) { using (provider) { return factory.CreateServiceProvider(factory.CreateBuilder(collection)); } } return provider; } } [MemberNotNull(nameof(_options))] private IServiceCollection BuildCommonServices(out AggregateException? hostingStartupErrors) { hostingStartupErrors = null; _options = new WebHostOptions(_config); if (!_options.PreventHostingStartup) { var exceptions = new List<Exception>(); var processed = new HashSet<Assembly>(); // Execute the hosting startup assemblies foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies()) { try { var assembly = Assembly.Load(new AssemblyName(assemblyName)); if (!processed.Add(assembly)) { // Already processed, skip it continue; } foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>()) { var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType)!; hostingStartup.Configure(this); } } catch (Exception ex) { // Capture any errors that happen during startup exceptions.Add(new InvalidOperationException($"Startup assembly {assemblyName} failed to execute. See the inner exception for more details.", ex)); } } if (exceptions.Count > 0) { hostingStartupErrors = new AggregateException(exceptions); } } var contentRootPath = ResolveContentRootPath(_options.ContentRootPath, AppContext.BaseDirectory); // Initialize the hosting environment ((IWebHostEnvironment)_hostingEnvironment).Initialize(contentRootPath, _options); _context.HostingEnvironment = _hostingEnvironment; var services = new ServiceCollection(); services.AddSingleton(_options); services.AddSingleton<IWebHostEnvironment>(_hostingEnvironment); services.AddSingleton<IHostEnvironment>(_hostingEnvironment); #pragma warning disable CS0618 // Type or member is obsolete services.AddSingleton<AspNetCore.Hosting.IHostingEnvironment>(_hostingEnvironment); services.AddSingleton<Extensions.Hosting.IHostingEnvironment>(_hostingEnvironment); #pragma warning restore CS0618 // Type or member is obsolete services.AddSingleton(_context); var builder = new ConfigurationBuilder() .SetBasePath(_hostingEnvironment.ContentRootPath) .AddConfiguration(_config, shouldDisposeConfiguration: true); _configureAppConfigurationBuilder?.Invoke(_context, builder); var configuration = builder.Build(); // register configuration as factory to make it dispose with the service provider services.AddSingleton<IConfiguration>(_ => configuration); _context.Configuration = configuration; services.TryAddSingleton(sp => new DiagnosticListener("Microsoft.AspNetCore")); services.TryAddSingleton<DiagnosticSource>(sp => sp.GetRequiredService<DiagnosticListener>()); services.TryAddSingleton(sp => new ActivitySource("Microsoft.AspNetCore")); services.TryAddSingleton(DistributedContextPropagator.Current); services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>(); services.AddTransient<IHttpContextFactory, DefaultHttpContextFactory>(); services.AddScoped<IMiddlewareFactory, MiddlewareFactory>(); services.AddOptions(); services.AddLogging(); services.AddMetrics(); services.TryAddSingleton<HostingMetrics>(); services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>(); if (!string.IsNullOrEmpty(_options.StartupAssembly)) { ScanAssemblyAndRegisterStartup(services, _options.StartupAssembly); } _configureServices?.Invoke(_context, services); return services; } [UnconditionalSuppressMessage("Trimmer", "IL2077", Justification = "Finding startup type in assembly requires unreferenced code. Surfaced to user in UseStartup(startupAssemblyName).")] [UnconditionalSuppressMessage("Trimmer", "IL2072", Justification = "Finding startup type in assembly requires unreferenced code. Surfaced to user in UseStartup(startupAssemblyName).")] private void ScanAssemblyAndRegisterStartup(ServiceCollection services, string startupAssemblyName) { try { var startupType = StartupLoader.FindStartupType(startupAssemblyName, _hostingEnvironment.EnvironmentName); if (typeof(IStartup).IsAssignableFrom(startupType)) { services.AddSingleton(typeof(IStartup), startupType); } else { services.AddSingleton(typeof(IStartup), RegisterStartup); [UnconditionalSuppressMessage("Trimmer", "IL2077", Justification = "Finding startup type in assembly requires unreferenced code. Surfaced to user in UseStartup(startupAssemblyName).")] object RegisterStartup(IServiceProvider serviceProvider) { var hostingEnvironment = serviceProvider.GetRequiredService<IHostEnvironment>(); var methods = StartupLoader.LoadMethods(serviceProvider, startupType, hostingEnvironment.EnvironmentName); return new ConventionBasedStartup(methods); } } } catch (Exception ex) { var capture = ExceptionDispatchInfo.Capture(ex); services.AddSingleton<IStartup>(_ => { capture.Throw(); return null; }); } } private static void AddApplicationServices(IServiceCollection services, IServiceProvider hostingServiceProvider) { // We are forwarding services from hosting container so hosting container // can still manage their lifetime (disposal) shared instances with application services. // NOTE: This code overrides original services lifetime. Instances would always be singleton in // application container. var listener = hostingServiceProvider.GetService<DiagnosticListener>(); services.Replace(ServiceDescriptor.Singleton(typeof(DiagnosticListener), listener!)); services.Replace(ServiceDescriptor.Singleton(typeof(DiagnosticSource), listener!)); var activitySource = hostingServiceProvider.GetService<ActivitySource>(); services.Replace(ServiceDescriptor.Singleton(typeof(ActivitySource), activitySource!)); } private static string ResolveContentRootPath(string? contentRootPath, string basePath) { if (string.IsNullOrEmpty(contentRootPath)) { return basePath; } if (Path.IsPathRooted(contentRootPath)) { return contentRootPath; } return Path.Combine(Path.GetFullPath(basePath), contentRootPath); } }