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 等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM