.net core WebHostBuilder 都做了什么?
源码
1 File: WebHostBuilder.cs
2 Web Access
3 Project: src\src\Hosting\Hosting\src\Microsoft.AspNetCore.Hosting.csproj (Microsoft.AspNetCore.Hosting)
376 // Licensed to the .NET Foundation under one or more agreements.
377 // The .NET Foundation licenses this file to you under the MIT license.
378
379 #nullable enable
380
381 using System.Diagnostics;
382 using System.Diagnostics.CodeAnalysis;
383 using System.Reflection;
384 using System.Runtime.ExceptionServices;
385 using Microsoft.AspNetCore.Hosting.Builder;
386 using Microsoft.AspNetCore.Http;
387 using Microsoft.Extensions.Configuration;
388 using Microsoft.Extensions.DependencyInjection;
389 using Microsoft.Extensions.DependencyInjection.Extensions;
390 using Microsoft.Extensions.Hosting;
391 using Microsoft.Extensions.Logging;
392
393 namespace Microsoft.AspNetCore.Hosting;
394
395 /// <summary>
396 /// A builder for <see cref="IWebHost"/>
397 /// </summary>
398 public class WebHostBuilder : IWebHostBuilder
399 {
400 private readonly HostingEnvironment _hostingEnvironment;
401 private readonly IConfiguration _config;
402 private readonly WebHostBuilderContext _context;
403
404 private WebHostOptions? _options;
405 private bool _webHostBuilt;
406 private Action<WebHostBuilderContext, IServiceCollection>? _configureServices;
407 private Action<WebHostBuilderContext, IConfigurationBuilder>? _configureAppConfigurationBuilder;
408
409 /// <summary>
410 /// Initializes a new instance of the <see cref="WebHostBuilder"/> class.
411 /// </summary>
412 public WebHostBuilder()
413 {
414 _hostingEnvironment = new HostingEnvironment();
415
416 _config = new ConfigurationBuilder()
417 .AddEnvironmentVariables(prefix: "ASPNETCORE_")
418 .Build();
419
420 if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.EnvironmentKey)))
421 {
422 // Try adding legacy environment keys, never remove these.
423 UseSetting(WebHostDefaults.EnvironmentKey, Environment.GetEnvironmentVariable("Hosting:Environment")
424 ?? Environment.GetEnvironmentVariable("ASPNET_ENV"));
425 }
426
427 if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.ServerUrlsKey)))
428 {
429 // Try adding legacy url key, never remove this.
430 UseSetting(WebHostDefaults.ServerUrlsKey, Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS"));
431 }
432
433 _context = new WebHostBuilderContext
434 {
435 Configuration = _config
436 };
437 }
438
439 /// <summary>
440 /// Get the setting value from the configuration.
441 /// </summary>
442 /// <param name="key">The key of the setting to look up.</param>
443 /// <returns>The value the setting currently contains.</returns>
444 public string? GetSetting(string key)
445 {
446 return _config[key];
447 }
448
449 /// <summary>
450 /// Add or replace a setting in the configuration.
451 /// </summary>
452 /// <param name="key">The key of the setting to add or replace.</param>
453 /// <param name="value">The value of the setting to add or replace.</param>
454 /// <returns>The <see cref="IWebHostBuilder"/>.</returns>
455 public IWebHostBuilder UseSetting(string key, string? value)
456 {
457 _config[key] = value;
458 return this;
459 }
460
461 /// <summary>
462 /// Adds a delegate for configuring additional services for the host or web application. This may be called
463 /// multiple times.
464 /// </summary>
465 /// <param name="configureServices">A delegate for configuring the <see cref="IServiceCollection"/>.</param>
466 /// <returns>The <see cref="IWebHostBuilder"/>.</returns>
467 public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices)
468 {
469 ArgumentNullException.ThrowIfNull(configureServices);
470
471 return ConfigureServices((_, services) => configureServices(services));
472 }
473
474 /// <summary>
475 /// Adds a delegate for configuring additional services for the host or web application. This may be called
476 /// multiple times.
477 /// </summary>
478 /// <param name="configureServices">A delegate for configuring the <see cref="IServiceCollection"/>.</param>
479 /// <returns>The <see cref="IWebHostBuilder"/>.</returns>
480 public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices)
481 {
482 _configureServices += configureServices;
483 return this;
484 }
485
486 /// <summary>
487 /// Adds a delegate for configuring the <see cref="IConfigurationBuilder"/> that will construct an <see cref="IConfiguration"/>.
488 /// </summary>
489 /// <param name="configureDelegate">The delegate for configuring the <see cref="IConfigurationBuilder" /> that will be used to construct an <see cref="IConfiguration" />.</param>
490 /// <returns>The <see cref="IWebHostBuilder"/>.</returns>
491 /// <remarks>
492 /// The <see cref="IConfiguration"/> and <see cref="ILoggerFactory"/> on the <see cref="WebHostBuilderContext"/> are uninitialized at this stage.
493 /// The <see cref="IConfigurationBuilder"/> is pre-populated with the settings of the <see cref="IWebHostBuilder"/>.
494 /// </remarks>
495 public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate)
496 {
497 _configureAppConfigurationBuilder += configureDelegate;
498 return this;
499 }
500
501 /// <summary>
502 /// Builds the required services and an <see cref="IWebHost"/> which hosts a web application.
503 /// </summary>
504 public IWebHost Build()
505 {
506 if (_webHostBuilt)
507 {
508 throw new InvalidOperationException(Resources.WebHostBuilder_SingleInstance);
509 }
510 _webHostBuilt = true;
511
512 var hostingServices = BuildCommonServices(out var hostingStartupErrors);
513 var applicationServices = hostingServices.Clone();
514 var hostingServiceProvider = GetProviderFromFactory(hostingServices);
515
516 if (!_options.SuppressStatusMessages)
517 {
518 // Warn about deprecated environment variables
519 if (Environment.GetEnvironmentVariable("Hosting:Environment") != null)
520 {
521 Console.WriteLine("The environment variable 'Hosting:Environment' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'");
522 }
523
524 if (Environment.GetEnvironmentVariable("ASPNET_ENV") != null)
525 {
526 Console.WriteLine("The environment variable 'ASPNET_ENV' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'");
527 }
528
529 if (Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS") != null)
530 {
531 Console.WriteLine("The environment variable 'ASPNETCORE_SERVER.URLS' is obsolete and has been replaced with 'ASPNETCORE_URLS'");
532 }
533 }
534
535 AddApplicationServices(applicationServices, hostingServiceProvider);
536
537 var host = new WebHost(
538 applicationServices,
539 hostingServiceProvider,
540 _options,
541 _config,
542 hostingStartupErrors);
543 try
544 {
545 host.Initialize();
546
547 // resolve configuration explicitly once to mark it as resolved within the
548 // service provider, ensuring it will be properly disposed with the provider
549 _ = host.Services.GetService<IConfiguration>();
550
551 var logger = host.Services.GetRequiredService<ILogger<WebHost>>();
552
553 // Warn about duplicate HostingStartupAssemblies
554 var assemblyNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
555 foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies())
556 {
557 if (!assemblyNames.Add(assemblyName))
558 {
559 logger.LogWarning($"The assembly {assemblyName} was specified multiple times. Hosting startup assemblies should only be specified once.");
560 }
561 }
562
563 return host;
564 }
565 catch
566 {
567 // Dispose the host if there's a failure to initialize, this should dispose
568 // services that were constructed until the exception was thrown
569 host.Dispose();
570 throw;
571 }
572
573 static IServiceProvider GetProviderFromFactory(IServiceCollection collection)
574 {
575 var provider = collection.BuildServiceProvider();
576 var factory = provider.GetService<IServiceProviderFactory<IServiceCollection>>();
577
578 if (factory != null && factory is not DefaultServiceProviderFactory)
579 {
580 using (provider)
581 {
582 return factory.CreateServiceProvider(factory.CreateBuilder(collection));
583 }
584 }
585
586 return provider;
587 }
588 }
589
590 [MemberNotNull(nameof(_options))]
591 private IServiceCollection BuildCommonServices(out AggregateException? hostingStartupErrors)
592 {
593 hostingStartupErrors = null;
594
595 _options = new WebHostOptions(_config);
596
597 if (!_options.PreventHostingStartup)
598 {
599 var exceptions = new List<Exception>();
600 var processed = new HashSet<Assembly>();
601
602 // Execute the hosting startup assemblies
603 foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies())
604 {
605 try
606 {
607 var assembly = Assembly.Load(new AssemblyName(assemblyName));
608
609 if (!processed.Add(assembly))
610 {
611 // Already processed, skip it
612 continue;
613 }
614
615 foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>())
616 {
617 var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType)!;
618 hostingStartup.Configure(this);
619 }
620 }
621 catch (Exception ex)
622 {
623 // Capture any errors that happen during startup
624 exceptions.Add(new InvalidOperationException($"Startup assembly {assemblyName} failed to execute. See the inner exception for more details.", ex));
625 }
626 }
627
628 if (exceptions.Count > 0)
629 {
630 hostingStartupErrors = new AggregateException(exceptions);
631 }
632 }
633
634 var contentRootPath = ResolveContentRootPath(_options.ContentRootPath, AppContext.BaseDirectory);
635
636 // Initialize the hosting environment
637 ((IWebHostEnvironment)_hostingEnvironment).Initialize(contentRootPath, _options);
638 _context.HostingEnvironment = _hostingEnvironment;
639
640 var services = new ServiceCollection();
641 services.AddSingleton(_options);
642 services.AddSingleton<IWebHostEnvironment>(_hostingEnvironment);
643 services.AddSingleton<IHostEnvironment>(_hostingEnvironment);
644 #pragma warning disable CS0618 // Type or member is obsolete
645 services.AddSingleton<AspNetCore.Hosting.IHostingEnvironment>(_hostingEnvironment);
646 services.AddSingleton<Extensions.Hosting.IHostingEnvironment>(_hostingEnvironment);
647 #pragma warning restore CS0618 // Type or member is obsolete
648 services.AddSingleton(_context);
649
650 var builder = new ConfigurationBuilder()
651 .SetBasePath(_hostingEnvironment.ContentRootPath)
652 .AddConfiguration(_config, shouldDisposeConfiguration: true);
653
654 _configureAppConfigurationBuilder?.Invoke(_context, builder);
655
656 var configuration = builder.Build();
657 // register configuration as factory to make it dispose with the service provider
658 services.AddSingleton<IConfiguration>(_ => configuration);
659 _context.Configuration = configuration;
660
661 services.TryAddSingleton(sp => new DiagnosticListener("Microsoft.AspNetCore"));
662 services.TryAddSingleton<DiagnosticSource>(sp => sp.GetRequiredService<DiagnosticListener>());
663 services.TryAddSingleton(sp => new ActivitySource("Microsoft.AspNetCore"));
664 services.TryAddSingleton(DistributedContextPropagator.Current);
665
666 services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>();
667 services.AddTransient<IHttpContextFactory, DefaultHttpContextFactory>();
668 services.AddScoped<IMiddlewareFactory, MiddlewareFactory>();
669 services.AddOptions();
670 services.AddLogging();
671
672 services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>();
673
674 if (!string.IsNullOrEmpty(_options.StartupAssembly))
675 {
676 ScanAssemblyAndRegisterStartup(services, _options.StartupAssembly);
677 }
678
679 _configureServices?.Invoke(_context, services);
680
681 return services;
682 }
683
684 [UnconditionalSuppressMessage("Trimmer", "IL2077", Justification = "Finding startup type in assembly requires unreferenced code. Surfaced to user in UseStartup(startupAssemblyName).")]
685 [UnconditionalSuppressMessage("Trimmer", "IL2072", Justification = "Finding startup type in assembly requires unreferenced code. Surfaced to user in UseStartup(startupAssemblyName).")]
686 private void ScanAssemblyAndRegisterStartup(ServiceCollection services, string startupAssemblyName)
687 {
688 try
689 {
690 var startupType = StartupLoader.FindStartupType(startupAssemblyName, _hostingEnvironment.EnvironmentName);
691
692 if (typeof(IStartup).IsAssignableFrom(startupType))
693 {
694 services.AddSingleton(typeof(IStartup), startupType);
695 }
696 else
697 {
698 services.AddSingleton(typeof(IStartup), RegisterStartup);
699
700 [UnconditionalSuppressMessage("Trimmer", "IL2077", Justification = "Finding startup type in assembly requires unreferenced code. Surfaced to user in UseStartup(startupAssemblyName).")]
701 object RegisterStartup(IServiceProvider serviceProvider)
702 {
703 var hostingEnvironment = serviceProvider.GetRequiredService<IHostEnvironment>();
704 var methods = StartupLoader.LoadMethods(serviceProvider, startupType, hostingEnvironment.EnvironmentName);
705 return new ConventionBasedStartup(methods);
706 }
707 }
708 }
709 catch (Exception ex)
710 {
711 var capture = ExceptionDispatchInfo.Capture(ex);
712 services.AddSingleton<IStartup>(_ =>
713 {
714 capture.Throw();
715 return null;
716 });
717 }
718 }
719
720 private static void AddApplicationServices(IServiceCollection services, IServiceProvider hostingServiceProvider)
721 {
722 // We are forwarding services from hosting container so hosting container
723 // can still manage their lifetime (disposal) shared instances with application services.
724 // NOTE: This code overrides original services lifetime. Instances would always be singleton in
725 // application container.
726 var listener = hostingServiceProvider.GetService<DiagnosticListener>();
727 services.Replace(ServiceDescriptor.Singleton(typeof(DiagnosticListener), listener!));
728 services.Replace(ServiceDescriptor.Singleton(typeof(DiagnosticSource), listener!));
729
730 var activitySource = hostingServiceProvider.GetService<ActivitySource>();
731 services.Replace(ServiceDescriptor.Singleton(typeof(ActivitySource), activitySource!));
732 }
733
734 private static string ResolveContentRootPath(string? contentRootPath, string basePath)
735 {
736 if (string.IsNullOrEmpty(contentRootPath))
737 {
738 return basePath;
739 }
740 if (Path.IsPathRooted(contentRootPath))
741 {
742 return contentRootPath;
743 }
744 return Path.Combine(Path.GetFullPath(basePath), contentRootPath);
745 }
746 }
747 Document OutlineProject ExplorerNamespace Explorer