zoukankan      html  css  js  c++  java
  • ASP.NET MVC入门-Program类:程序的入口

    笔记内容来源于微软 MVP 杨旭老师 solenovex 的视频

    Program 类:Web程序的入口

    Program 类的本质就是一个控制台应用。

    其中的 Main 方法是 Web 应用的入口方法。

    我们可以在 Main方法中启动 Web 服务。

    Program 类的源码

    首先,Program 类中的 Main方法调用了 CreateWebHostBuilder(args).Build().Run() 方法。

    该方法的最终目的和意义就是使用 CreateWebHostBuilder 方法返回的 IWebHostBuilder 接口类型对象来构建(Build)一个 WebServe 然后运行(Run)这个 WebServe

    public class Program
    {
      public static void Main(string[] args)
      {
        CreateWebHostBuilder(args).Build().Run();
      }
      public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>();
    }

    CreateWebHostBuilder 方法返回的对象是静态类WebHost 的静态方法 CreateDefaultBuilder 方法返回的。

    CreateWebHostBuilder 方法中我们可以看到一个 lambda 表达式,他返回了 WebHost.CreateDefaultBuilder(args).UseStartup<Startup>(); 

    CreateDefaultBuilder 方法的源码

    反编译 WebHost.CreateDefaultBuilder 方法的源码,我们先来看一下完整的代码实现。

    我们发现 CreateDefaultBuilder 方法首先 new 了一个 WebHostBuilder 类型的对象  var builder = new WebHostBuilder(); 。  

    而 WebHostBuilder 类就实现了我们所需要返回的的 IWebHostBuilder 接口类型。

    public static IWebHostBuilder CreateDefaultBuilder(string[] args)
    {
        var builder = new WebHostBuilder();
        if (string.IsNullOrEmpty(builder.GetSetting(WebHostDefaults.ContentRootKey)))
        {
            builder.UseContentRoot(Directory.GetCurrentDirectory());
        }
        if (args != null)
        {
            builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build());
        }
        builder.ConfigureAppConfiguration((hostingContext, config) =>
        {
            var env = hostingContext.HostingEnvironment;
            config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
            if (env.IsDevelopment())
            {
                var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
                if (appAssembly != null)
                {
                    config.AddUserSecrets(appAssembly, optional: true);
                }
            }
            config.AddEnvironmentVariables();
            if (args != null)
            {
                config.AddCommandLine(args);
            }
        })
        .ConfigureLogging((hostingContext, logging) =>
        {
            logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
            logging.AddConsole();
            logging.AddDebug();
            logging.AddEventSourceLogger();
        }).
        UseDefaultServiceProvider((context, options) =>
        {
            options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
        });
        ConfigureWebDefaults(builder);
        return builder;
    }

    ConfigureWebDefaults 方法的源码

    在方法的末尾,调用了 ConfigureWebDefaults(builder); 方法。

    该方法详细地配置了默认的 builder 的各种设置,我们也来看一下完整的源码实现。

    internal static void ConfigureWebDefaults(IWebHostBuilder builder)
    {
        builder.UseKestrel((builderContext, options) =>
        {
            options.Configure(builderContext.Configuration.GetSection("Kestrel"));
        })
        .ConfigureServices((hostingContext, services) =>
        {
            // Fallback
            services.PostConfigure<HostFilteringOptions>(options =>
            {
                if (options.AllowedHosts == null || options.AllowedHosts.Count == 0)
                {
                    // "AllowedHosts": "localhost;127.0.0.1;[::1]"
                    var hosts = hostingContext.Configuration["AllowedHosts"]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                    // Fall back to "*" to disable.
                    options.AllowedHosts = (hosts?.Length > 0 ? hosts : new[] { "*" });
                }
            });
            // Change notification
            services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>(
                        new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration));
    
            services.AddTransient<IStartupFilter, HostFilteringStartupFilter>();
        })
        .UseIIS()
        .UseIISIntegration();
    }

    builder 的默认配置

    1.使用 Kestrel Web Serve

    如上代码所示,我们可以看到 builder 首先调用了 UseKestrel 方法,配置了 Kestrel 服务器。

    2.IIS 集成

    然后 builder  使用了 UseIIS().UseIISIntegration(); 这两个方法配置了 IIS 服务器。

    UseIISIntegration 方法和前者的区别就是它允许 IIS 通过 Windows 的验证来到 Kestrel 服务器,这有利于构建内网的 Web 应用。

    而在项目的 csproj 文件中我们可以看到声明了 <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>  。

    InProcess 模式下 IIS 会和 Web 程序运行在一个系统内。而与之相对的是 OutOfProcess 模型。

    OutOfProcess 模式会把请求转发给 Kestrel 服务器。相比 OutOfProcess 这种分开的模式,InProcess 模式提高了性能。

    3.Logger

    回到 CreateDefaultBuilder 方法,这段代码配置了 ConfigureLogging,将 logger 输出到 ConsoleDebug EventSourceLogger。

    .ConfigureLogging((hostingContext, logging) =>
    {
        logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
        logging.AddConsole();
        logging.AddDebug();
        logging.AddEventSourceLogger();
    }).

    4.IConfiguration 接口

    我们可以通过实现 IConfiguration 接口的类型对象里获取一些需要的配置信息。

    前面提到 CreateDefaultBuilder 方法首先 new 了一个 WebHostBuilder 类型的对象。  var builder = new WebHostBuilder(); 

    而 WebHostBuilder 类型就持有一个 IConfiguration 接口类型的成员字段 _config

    IConfiguration 接口类型也很简单,其中的 string this[string key]{get; set;} 就是以 key-value 的形式,通过string来获取配置信息,封装的其他方法就在此省略了。

    配置信息首先从json文件中获取,我们的项目中存在着 appsettings.json 和 appsettings.Development.json 两个默认的 json 文件。

    打开 appsettings.json 文件,我们可以看到默认生成的配置如下所示。

    {
      "Logging": {
        "LogLevel": {
          "Default": "Warning"
        }
      },
      "AllowedHost": "*"
    }

    获取配置信息的方法和顺序

    回到 CreateDefaultBuilder 方法,我们可以找到这一句代码。

    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);

    其中,config 是 builder.ConfigureAppConfiguration() 调用的 lambda 表达式参数   (hostingContext, config) =>{}  ,其设置了配置文件的具体名称。

    注意,config 调用 AddJsonFile 方法,调用了两次,第二个 json 文件是系统环境变量为名的json文件,默认是 Devolopment,也就是前面提到的 appsettings.Development.json

    如果两个文件有相同的属性,后者就会覆盖掉前者的设置。但是 CreateDefaultBuilder 方法的代码中还有一句设置 config.AddEnvironmentVariables(); 

    这一句代码是设置系统环境变量,如果有和以上配置文件相同的属性,也会覆盖的前者的设置。

    在系统环境变量之后又有一句 config.AddCommandLine(args); ,命令行中如果有和以上配置文件相同的属性,也会覆盖的前者的设置。

    所以最终的属性的覆盖顺序为 appsettings.json -> appsettings.{env.EnvironmentName}.json -> AddEnvironmentVariables -> AddCommandLine

    UseStartup<> 方法

    回到 Program 类的 CreateWebHostBuilder 方法,我们可以发现 WebHost.CreateDefaultBuilder(args).UseStartup<Startup>(); 。

    其调用了 UseStartup<> 方法。 UseStartup<Startup>()  指定 StartupStartup 类,以该类来配置整个 Web 应用,如注册服务、使用中间件等等。

    然后CreateWebHostBuilder 方法返回的 builder 回到 Main 方法,Build 后再调用 Run 方法,我们的 Web 应用就可以监听 Http/Https 的请求了。

    Progarm 这个类主要是为了配置我们的 Web 应用的基础设施,如 Http 服务器以及如何集成到 IIS 等。

  • 相关阅读:
    架构设计:系统存储(10)——MySQL简单主从方案及暴露的问题
    西安大唐提车游记——感受古都容颜
    架构设计:系统存储(9)——MySQL数据库性能优化(5)
    架构设计:系统存储(8)——MySQL数据库性能优化(4)
    架构设计:系统存储(7)——MySQL数据库性能优化(3)
    架构设计:系统存储(6)——MySQL数据库性能优化(2)
    全班成绩录入系统
    直接选择排序
    冒泡排序
    直接插入排序
  • 原文地址:https://www.cnblogs.com/errornull/p/10534612.html
Copyright © 2011-2022 走看看