zoukankan      html  css  js  c++  java
  • asp.net core 3.1 源码学习之 GenericWebHostBuilder

    先看下如下的扩展方法

    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开始处理请求

  • 相关阅读:
    【学习总结】测试开发工程师面试指南-软件测试行业分析与职业解析
    【学习总结】测试开发工程师面试指南-汇总
    【JAVA】java中char类型数组用数组名打印结果不是地址值而是数组内容
    Python常见问题合集
    操作系统常见问题合集
    算法题常见问题合集
    个人向常见问题合集
    Linux常见问题合集
    数据结构常见问题合集
    网络常见问题合集
  • 原文地址:https://www.cnblogs.com/lanpingwang/p/12640734.html
Copyright © 2011-2022 走看看