• 应用--WebApplication


    应用--Program中的WebApplication

    在6.0,微软团队对于NetCore做了很大的改变,其中有一个改变就是推出了新的托管模型--最小托管模型,使用该模型可以创建最小的web应用。(最小webapi请查看官网

    需要掌握:

    构造流程#

    得到WebApplicationBuilder构造器 -> 配置服务 -> build()方法得到WebApplication对象 -> 配置中间件 -> 运行主机

    // 得到应用构造器:WebApplicationBuilder
    var builder = WebApplication.CreateBuilder(args);
    // 配置日志
    builder.Logging.AddLog4Net("ConfigFile/log4net.config");
    // 得到应用:WebApplication
    var app = builder.Build();
    // 配置中间件
    app.UseStaticFiles();
    // 运行主机
    app.Run();
    

    你可能在疑惑,在3.0至5.0的版本都是直接调用Host.CreateDefaultBuilder()方法得到HostBuilder构造器,然后调用ConfigureWebHostBuilder()配置WebHost,然后在上面配置一些服务,构建然后运行。而6.0使用WebApplication.CreateBuilder(args)方法得到的是一个WebApplicationBuilder构造器,然后构建运行,他们有什么区别吗?

    答:没什么区别,流程都是一样的,WebApplication对主机和服务做了一个更进一步的封装,使得更加方便配置和学习,而且额外暴露2个Host属性WebHost属性用来配置(这2个属性也是方便之前的版本迁移到6.0的关键)。举个很简单的例子

    区别 在3.0至5.0的版本中 6.0版本中
    中间管道的配置 必须放在Startup.cs类中的Configure方法中,或者通过ConfigureWebHostDefaults中的webBuilder 来配置服务 通过调用app.UseXXXX来配置
    路由中间件 使用app.UseRouting()之后才能app.UseEndpoints() 因为WebApplication继承了WebIEndpointRouteBuilder可以直接将路由,而无需显式调用 UseEndpointsUseRouting

    按照上方Program.cs流程顺序介绍相关类和方法#

    1. CreateBuilder(args) 方法:#

    使用默认值来生成构造一个WebApplicationBuilder对象

    该方法有3个重载:

    • WebApplication.CreateBuilder():使用预配置的默认值来构造;
    • WebApplication.CreateBuilder(String []):根据传入的命令行参数初始化;
    • WebApplication.CreateBuilder(WebApplicationOption):根据传入的预配置来构造;
    public class WebApplicationOptions
    {
        public WebApplicationOptions(); 
        
        // 命令行参数
        public string[]? Args { get; init; }
    
        // 环境名称。
        public string? EnvironmentName { get; init; }
        
        // 应用程序名称。
        public string? ApplicationName { get; init; }
        
        // 内容根路径。
        public string? ContentRootPath { get; init; }
    
        // Web 根路径。
        public string? WebRootPath { get; init; }
    }
    

    WebApplicationBuilder类:#

    要创建一个WebApplication对象,需要一个IHost对象IHost对象是通过IHostBuilder创建的,而WebApplication需要WebApplicationBuilder来构建,所以WebApplicationBuilder还需要一个IHostBuilder对象,我们针对WebApplication的一切配置,最终都会转移到这个对象上面才能生效,所以这就是为什么WebApplicationBuilder提供了这6个属性的原因。

    构造函数

    当通过调用WebApplication.CreateBuilder()方法的时候,根据命令行的参数传给WebApplicationBuilder的构造函数,而WebApplicationBuilder的构造函数内部会做:

    1. 创建HostBuilder _hostBuilder 类。
    2. 创建BootstrapHostBuilder对象,调用拓展方法ConfigureWebHostBuilder()ConfigureDefaults()方法,将初始化的设置和服务收集起来,然后把收集到的服务和配置注入到Services成员属性Configure成员属性中。
    3. 然后会创建承载托管环境的IWebHostEnvironment,对于Environment成员属性初始化。
    4. 调用Apply()方法得到HostBuilderContext上下文
    5. 使用HostBuilderContextWebHostBuilderContext,创建ConfigureWebHostBuilder和ConfigureHostBuilder并赋值给WebHost和Host属性。初始化Logging属性
    6. 得到一个Configure、Environment、WebHost、Host、Logging属性都被初始化的WebApplication对象

    WebApplicationBuilder构造函数源码

    public class WebApplicationBuilder
    {
        private readonly HostBuilder _hostBuilder = new HostBuilder();
        private WebApplication _application;
        // 提供应用程序正在运行的Web托管环境的信息
        public IWebHostEnvironment Environment { get; }
    
        // 提供应用程序所需要的服务,即依赖注入容器
        public IServiceCollection Services { get; }
    
        // 提供应用程序所需要的配置
        public ConfigurationManager Configuration { get; }
    
        // 提供日志记录
        public ILoggingBuilder Logging { get; }
    
        // 配置WebHost服务器特定属性,实现IWebHostBuilder
        public ConfigureWebHostBuilder WebHost { get; }
    
        // 配置Host特定属性,实现IHostBuilder
        public ConfigureHostBuilder Host { get; }
    
        public WebApplicationBuilder(WebApplicationOptions options)
        {
            //创建BootstrapHostBuilder并利用它收集初始化过程中设置的配置、服务和针对依赖注入容器的设置
            var args = options.Args;
            var bootstrap = new BootstrapHostBuilder();
            bootstrap
                .ConfigureDefaults(null)
                // 此处用于中间件的注册
                .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder.Configure(app              =>app.Run(_application.BuildRequestDelegate())))   
                .ConfigureHostConfiguration(config => {
                    // 添加命令行配置源
                    if (args?.Any() == true)
                    {
                        config.AddCommandLine(args);
                    }
    
                    // 将WebApplicationOptions配置选项转移到配置中
                    Dictionary<string, string>? settings = null;
                    if (options.EnvironmentName is not null) {
                        (settings ??= new())[HostDefaults.EnvironmentKey] =  options.EnvironmentName;
                    }
                    if (options.ApplicationName is not null){
                        (settings ??= new())[HostDefaults.ApplicationKey] = options.ApplicationName;
                    }
                    if (options.ContentRootPath is not null){
                        (settings ??= new())[HostDefaults.ContentRootKey] = options.ContentRootPath;
                    }
                    if (options.WebRootPath is not null) {
                        (settings ??= new())[WebHostDefaults.WebRootKey] = options.EnvironmentName;
                    }
                    if (settings != null)
                    {
                        config.AddInMemoryCollection(settings);
                    }
                });
    
            // 将BootstrapHostBuilder收集到配置和服务转移到Configuration和Services上
            // 将应用到BootstrapHostBuilder上针对依赖注入容器的设置转移到_hostBuilder上
            // 得到BuilderContext上下文
            bootstrap.Apply(_hostBuilder, Configuration, Services, out var builderContext);
    
            // 如果提供了命令行参数,在Configuration上添加对应配置源
            if (options.Args?.Any() == true)
            {
                Configuration.AddCommandLine(options.Args);
            }
            // 构建WebHostBuilderContext上下文
            // 初始化Host、WebHost和Logging属性
            var webHostContext = (WebHostBuilderContext)builderContext.Properties[typeof(WebHostBuilderContext)];
            Environment = webHostContext.HostingEnvironment;
            Host = new ConfigureHostBuilder(builderContext, Configuration, Services);
            WebHost = new ConfigureWebHostBuilder(webHostContext, Configuration, Services);
            Logging = new LogginigBuilder(Services);
        }
       
    }
    

    简单来说WebApplicationBuilder的作用就是为了提供构建封装的一个HostBuilder对象

    注意:记住这6个属性,NetCore的生态库基本上就是围绕这几个来构建的

    接下来我们按照这几个属性来逐一分析其作用

    IWebHostEnvironment Environment 属性

    接口,继承IHostEnvironment,提供一些该应用程序的环境信息,比如根目录、环境变量、名称等,包含几个属性

    • WebRootPath,用于设置和获取Web的根目录,默认是wwwroot的子文件夹(在5.0的时候。是通过Host.CreateDefaultBuilder()方法去设置的);
    • ApplicationName:应用名称;
    • ContentRootFileProvider:;
    • ContentRootPath:
    • EnvironmentName:环境名称;

    比如我们经常用的判断是否是开发环境就是用的该类

    demo

    var builder = WebApplication.CreateBuilder(args); 
    bool isDevelopment = builder.Environment.IsDevelopment();
    
    IServiceCollection Services 属性

    依赖注入容器,可以注入服务,也可也获取服务实例,继承于ICollection泛型接口,这个我们后续会在依赖注入章节详细描述。

    通过往该属性添加系统服务支持,或者注入自己的服务

    demo

    var builder = WebApplication.CreateBuilder(args); 
    // 注入Sql数据库支持
    builder.Services.AddDbContext();
    // 依赖注入
    builder.Services.AddSingleton();
    
    ConfigurationManager Configuration 属性

    配置管理,是6.0版本新增的类,更见简单的用于获取和设置系统配置文件,替换掉了5.0版本中IConfigurationBuilder接口IConfigurationRoot接口(6.0是把这2个接口整合在一起了,看源码可以发现ConfigureManager继承于这2个接口)

    ConfigurationManager密封类源码

    public sealed class ConfigurationManager : IConfigurationBuilder, IConfigurationRoot, IConfiguration, IDisposable
    {
        public ConfigurationManager();
        public string this[string key] { get; set; }
        public void Dispose();
        public IEnumerable GetChildren();
        public IConfigurationSection GetSection(string key);
    }
    
    public interface IConfigurationBuilder
    { 
      IDictionary<string, object> Properties { get; }  
      IList Sources { get; }
      IConfigurationBuilder Add(IConfigurationSource source);
      IConfigurationRoot Build();
    }
    

    增加系统配置文件

    当你调用builder.Configuration.AddJsonFile("文件名称")拓展方法的来增加自定义配置文件的时候(实际上是往调用的IConfigurationBuilder.Add()方法往IList Sources { get; }属性增加了一条数据),会将立即加载提供程序并更新配置,这样可以不用等到Build()方法,可以避免在部分生成方法多次加载配置源数据。

    demo

    var builder = WebApplication.CreateBuilder(args); 
    builder.Configuration.AddJsonFile("servicesetting.json");
    

    获取系统配置数据

    demo

    var builder = WebApplication.CreateBuilder(args); 
    ConfigurationManager config = builder.Configuration;
    string value1 = config["DBContextModel:SqlConnection"];
    IConfigurationSection value2 = config.GetSection("DBContextModel");
    
    ILoggingBuilder Logging

    提供日志记录,包括控制台、调试、事件日志、TraceSource等组件,你是不是在疑惑为什么创建项目的时候appsetting.json配置文件有一个这样的配置?他就和日志记录息息相关

    appstting.json文件节点

     "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      },
    

    具体在日志章节描述

    ConfigureWebHostBuilder WebHost 属性

    继承于IWebHostBuilder接口,目的是复用接口,继承于ISupportsStartUp接口,由于6.0使用的是最小托管模型,所以传统的使用Startup.cs文件来配置服务和注册中间件已经不支持了,继承此接口的是原因是:

    微软原话“但是我们希望用户在采用这种编程方式时得到显式的提醒,所以依然让它实现该接口,并在实现的方法中抛出NotImplementedException类型的异常。”

    WebApplication构造函数中,通过传进来的一些构造参数初始化一个实例赋值给该属性;

    构造函数,通过WebHostContext,Services成员属性、Configure成员属性来初始化

    ConfigureWebHostBuilder类源码

    public class ConfigureWebHostBuilder : IWebHostBuilder, ISupportsStartup
    {
        private readonly WebHostBuilderContext _builderContext;
        private readonly IServiceCollection _services;
        private readonly ConfigurationManager _configuration;
    
        public ConfigureWebHostBuilder(WebHostBuilderContext builderContext, ConfigurationManager configuration, IServiceCollection services)
        {
            _builderContext = builderContext;
            _services = services;
            _configuration = configuration;
        }
    }
    
    ConfigureHostBuilder Host 属性

    继承于IHostBuilder接口,目的是复用接口,他更多的用来配置主机服务

    WebApplication构造函数中,通过bootStrapBuilder收集到的服务,传进来的一些构造参数初始化一个实例赋值给他;

    构造函数,通过HostBuilderContext ,Services成员属性、Configure成员属性来初始化

    • Services成员属性会直接赋值ConfigureHostBuilder的_services属性

    • Configure成员属性相关Host的配置会被存放在ConfigureHostBuilder内部类的一个_configureActions字段暂时存起来

    ConfigureHostBuilder类源码

    public class ConfigureHostBuilder : IHostBuilder
    {
        private readonly ConfigurationManager _configuration;
        private readonly IServiceCollection _services;
        private readonly HostBuilderContext _context;
        private readonly List> _configureActions = new();
    
        internal ConfigureHostBuilder(HostBuilderContext context, ConfigurationManager configuration, IServiceCollection services)
        {
            _configuration = configuration;
            _services = services;
            _context = context;
        }
    }
    

    BootstrapHostBuilder类:#

    BootstrapHostBuilder继承于IHostBuilder,目的是为了构建和初始化IHostBuilder对象

    这个它的作用是收集初始化IHostBuilder对象提供的设置并将它们分别应用到指定的IServiceCollection、ConfigurationManager和IHostBuilder对象上,在构造函数中会调用他的Apply()方法。

    2. Build()方法:#

    简单来说就是将对于WebApplicationBuilder的一切配置转移到IHostBuilder对象上,然后得到一个WebApplication对象

    注意!!!!!

    WebApplication一旦创建,环境变量、配置都不允许再次改变(虽然我们也用不着,但是知道就好)

    这个方法作用:

    WebApplicationConfigure成员属性Services成员属性转移到HostBuilder上面

    WebApplication.Build()方法源码

     // 获取WebApplication对象,用于配置 HTTP 管道和路由的 Web 应用程序
        public WebApplication Build()
        {
            // 在此处连接主机配置。我们不会尝试保留配置,在此处获取本身,因为我们不支持在创建构建器后更改主机值。
             _hostBuilder.ConfigureHostConfiguration(builder =>
                {
                    builder.AddInMemoryCollection(_hostConfigurationValues);
              });
            
            // 将ConfigurationManager的配置转移到_hostBuilder
            _hostBuilder.ConfigureAppConfiguration(builder =>
            {
                builder.AddConfiguration(Configuration);
                foreach (var kv in ((IConfigurationBuilder)Configuration).Properties)
                {
                    builder.Properties[kv.Key] = kv.Value;
                }
            });
    
             var chainedConfigSource = new TrackingChainedConfigurationSource(Configuration);
            
            _hostBuilder.ConfigureServices((context, services) =>
                {
                    // 简单来说就是把WeApplicationBuilder中的IServiceCollection属性添加到泛型主机中
                    foreach (var s in _services)
                    {                
                        services.Add(s);
                    }
    
                    // 把服务列表只能关于主机的服务添加到主机中
                    // 确保添加的任何托管服务在初始托管服务集之后运行。也就是托管服务在web主机启动前运行
                    foreach (var s in _services.HostedServices)
                    {
                        services.Add(s);
                    }
    
                    // 清除主机托管服务列表
                    _services.HostedServices.Clear();
    
                    // 将任何服务添加到用户可见的服务集合中,
                    _services.InnerCollection = services;
    
                   // 保留主机中的配置
                    var beforeChainedConfig = true;
                    var hostBuilderProviders = ((IConfigurationRoot)context.Configuration).Providers;
    
                    if (!hostBuilderProviders.Contains(chainedConfigSource.BuiltProvider))
                    {                   
                        ((IConfigurationBuilder)Configuration).Sources.Clear();
                        beforeChainedConfig = false;
                    }
                    // 使配置管理器与最终_hostBuilder的配置匹配。
                    foreach (var provider in hostBuilderProviders)
                    {
                        if (ReferenceEquals(provider, chainedConfigSource.BuiltProvider))
                        {
                            beforeChainedConfig = false;
                        }
                        else
                        {
                            IConfigurationBuilder configBuilder = beforeChainedConfig ? _hostConfigurationManager : Configuration;
                            configBuilder.Add(new ConfigurationProviderSource(provider));
                        }
                    }
                });
    
            // 在最终主机构建器上运行其他回调
            Host.RunDeferredCallbacks(_hostBuilder);
    
            // 构建应用
            _builtApplication = new WebApplication(_hostBuilder.Build());
            
            // 将服务集合标记为只读以防止将来修改
             _services.IsReadOnly = true;
    
            // 解析_hostBuilder的配置和构建器。 
            _ = _builtApplication.Services.GetService>();
            return _builtApplication;
        }
    

    WebApplication类:#

    应用类

    继承4个接口#

    • IHost接口:所以这就是上文当中说到为什么WebApplicationBuilder需要一个IConfigureHostBuilder属性的原因;
    • IApplicationBuilder:提供配置应用程序请求管道机制的类,所以我们的中间件可以直接注册到WebApplication
    • IEndpointRouteBuilder:定义应用程序中路由生成器的协定。 路由生成器指定应用程序的路由。所以我们无需显示调用UseEndpoint、UseRouting这2个中间件,这个在6.0的更新中也提到了;
    • IAsyncDisposable:提供异步释放的接口;

    6个重要属性#

    • IServiceProvider:应用程序的已配置服务。提供在程序运行期间解析的服务类型,简称依赖注入容器;
    • IConfiguration:应用程序的已配置,可以获取已经配置好的配置源;
    • IWebHostEnvironment: 托管环境信息;
    • IHostApplicationLifetime:允许通知使用者应用程序生存期事件;
    • ILogger:日志服务;
    • ICollection :HTTP 服务器绑定到的 URL 列表。(IServerAddressesFeature:启动地址);

    WebApplication类源码

    public sealed class WebApplication : IHost, IApplicationBuilder, IEndpointRouteBuilder, IAsyncDisposable
    {
       public IServiceProvider Services => _host.Services;   
       public IConfiguration Configuration => _host.Services.GetRequiredService();
       public IWebHostEnvironment Environment => _host.Services.GetRequiredService();
       public IHostApplicationLifetime Lifetime => _host.Services.GetRequiredService();
       public ILogger Logger { get; } 
       public ICollection<string> Urls => ServerFeatures.Get()?.Addresses ??
                throw new InvalidOperationException($"{nameof(IServerAddressesFeature)} could not be found.");
    
    }
    

    拓展方法#

    就是各种系统定义好的中间件服务。

    3. Run()方法 :#

    WebApplication.BuildRequestDelegate()方法#

    前面调用ConfigureWebHostDefaults()扩展方法提供的委托会将使用BuildRequestDelegate()方法注册的中间件管道,作为请求处理器,至此一个WebApplication对象完成。

    ## 4. 总结 :
    首先,最小托管模型是6.0微软推出来的一个新的应用模板,为的是方便配置和学习,他只有3句代码,利用他可以生成最小webapi。
    第一句是var bulider = WebAppliaction.CreateBuilder();
    这句代码的作用是通过调用WebAppliaction的工厂方法CreateBuilder()得到WebApplicationBuilder对象,因为创建一个WebApplication对象需要一个Host,Host则必须由HostBuilder创建,所以WebApplicationBuilder对象的作用是提供一个封装好的HostBuilder对象用来构建IHost,
    它含有6个属性以及一个构造函数,属性包括IServiceCollection依赖注入容器Services、ConfigureManage配置管理Configure、IWebHostEnvironment托管环境environment、ILoggingBuilder日志记录logging、ConfigureWebHostBuilder类型webhost、ConfigureHostBuilder类型host,需要这6个属性的目的就是用来提供HostBuilder的创建
    构造函数的作用是根据传进来的命令行参数来初始化这些属性,首先他会初始化一个_hostBuilder对象,然后创建一个bootstrapBuilder对象用来调用他的拓展方法收集服务和配置,赋值给services和configure属性,接下里根据bootstrapBuilder对象的一些属性,初始化剩余属性,初始化WebHost和Host。
    
    第二句代码是var app = bulider.bulider();
    在这句代码之前,我们可以注入自己的一些服务和系统服务,通过调用AddScope()等依赖注入方法或者使用系统提供的服务方法、如AddController(),
    这句代码的作用是,根据得到WebApplicationBuilder对象来创建WebApplication,这句代码最重要的就是,把services和configure属性赋值给HostBuilder,然后我们可以看到这个对象继承了4个接口,一个是IHost接口,这就解释了为什么WebApplicationBuilder需要有一个ConfigurHostBuilder对象,还有一个IApplicationBuilder接口,这个接口是构建中间件管道服务的接口,所以我们的中间件可以直接注册在WebApplication的原因,IEndpointRouteBuilder则是默认构造了路由,还有一个异步释放的接口。
    
    第三句代码是app.Run();
    在这句代码之前可以注入中间件服务,比如UseAuthorization()之类的,
    这句代码的作用是,通过调用WebApplication内部的BuildRequestDelegation()方法把注册的中间件管道作为请求处理器,至此一个WebApplication对象完成
    所以根据这几行代码我们不难看出,WebApplication其实就是对Host的再次封装,只是为了我们更加简单的去配置一些我们需要的服务和中间件
    
  • 相关阅读:
    网络技术-安全评估技术
    vueRouter 重定向 高亮 传参 嵌套 简单示例
    使用uniapp实现小程序获取wifi并连接
    读书充电,温暖你的冬日。不可错过的10本架构师必读书籍
    入门【网络安全/黑客】启蒙教程
    杰理之、产线装配环节【篇】
    我的 ReactNative 开发利器: 常用命令总结
    如何应对 CentOS 的停更?
    uniapp集成个推
    第二章:25+ Python 数据操作教程(第十八节如何使用 Matplotlib 库在 python 中执行绘图和数据可视化)持续更新中
  • 原文地址:https://www.cnblogs.com/boise/p/18002731