ASP.NET Core - 從Program和Startup開始


  Program  

  我們先看一下1.x和2.x的程序入口項的一個差異

  1.x

public class Program
{
    public static void Main(string[] args)
     {
        var host = new WebHostBuilder()
               .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

        host.Run();
     }
} 

  2.x

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();
}

  2.x對默認配置進行了簡化,把一些基本配置移動了 CreateDefaultBuilder 方法中  

  public static IWebHostBuilder CreateDefaultBuilder(string[] args)
    {
      IWebHostBuilder hostBuilder = new WebHostBuilder()
    .UseKestrel((Action<WebHostBuilderContext, KestrelServerOptions>) ((builderContext, options) => options.Configure((IConfiguration) builderContext.Configuration.GetSection("Kestrel"))))
    .UseContentRoot(Directory.GetCurrentDirectory())
    .ConfigureAppConfiguration((Action<WebHostBuilderContext, IConfigurationBuilder>) ((hostingContext, config) => { IHostingEnvironment hostingEnvironment = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", true, true)
      .AddJsonFile("appsettings." + hostingEnvironment.EnvironmentName + ".json", true, true); if (hostingEnvironment.IsDevelopment()) { Assembly assembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName)); if (assembly != (Assembly) null) config.AddUserSecrets(assembly, true); } config.AddEnvironmentVariables(); if (args == null) return; config.AddCommandLine(args); }))
    .ConfigureLogging((Action
<WebHostBuilderContext, ILoggingBuilder>) ((hostingContext, logging) => { logging.AddConfiguration((IConfiguration) hostingContext.Configuration.GetSection("Logging")); logging.AddConsole(); logging.AddDebug(); }))
    .ConfigureServices((Action
<WebHostBuilderContext, IServiceCollection>) ((hostingContext, services) => { services.PostConfigure<HostFilteringOptions>((Action<HostFilteringOptions>) (options => { if (options.AllowedHosts != null && options.AllowedHosts.Count != 0) return; string str = hostingContext.Configuration["AllowedHosts"]; string[] strArray1; if (str == null) strArray1 = (string[]) null; else strArray1 = str.Split(new char[1]{ ';' }, StringSplitOptions.RemoveEmptyEntries); string[] strArray2 = strArray1; HostFilteringOptions filteringOptions = options; string[] strArray3; if (strArray2 == null || strArray2.Length == 0) strArray3 = new string[1]{ "*" }; else strArray3 = strArray2; filteringOptions.AllowedHosts = (IList<string>) strArray3; })); services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>((IOptionsChangeTokenSource<HostFilteringOptions>) new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration)); services.AddTransient<IStartupFilter, HostFilteringStartupFilter>(); }))
    .UseIISIntegration()
    .UseDefaultServiceProvider((Action
<WebHostBuilderContext, ServiceProviderOptions>) ((context, options) => options.ValidateScopes = context.HostingEnvironment.IsDevelopment())); if (args != null) hostBuilder.UseConfiguration((IConfiguration) new ConfigurationBuilder().AddCommandLine(args).Build()); return hostBuilder; }

   這里我們可以看到在CreateDefaultBuilder生成器中,定義了默認使用的Web服務器(UseKestrel,使用的是KestrelServer)和一些基礎的配置,包括文件路徑、應用配置(按appsettings.json,appsettings.{Environment}.json次序加載)、環境變量、日志,IIS集成等,如果需要的話,還可以指定其他類型的Server(IIS HTTP Server,HTTP.sys Server)和自定義Server(繼承IServer)。

  返回到Program中,在獲取到了WebHostBuilder之后緊接着就指定了啟動類UseStartup<Startup>(),Build方法是WebHostBuilder最終的目的(在這個方法里面構建了管道),將構造一個WebHost返回,這里引出了我們在ASP.NET Core - 開篇所說的重要對象:WebHost,並且運行它的Run方法用於啟動應用並開始監聽所有到來的HTTP請求。

   Startup

  Startup方法用來指定應用程序的啟動類,這里主要有兩個作用:

  1. 配置應用需要的服務(服務注冊,ConfigureServices方法)。
  2. 創建應用的請求處理處理管道(Configure方法)。
public class Startup
{
   private readonly IHostingEnvironment _env;
    private readonly IConfiguration _config;
    private readonly ILoggerFactory _loggerFactory;

    public Startup(IHostingEnvironment env, IConfiguration config, 
        ILoggerFactory loggerFactory)
    {
        _env = env;
        _config = config;
        _loggerFactory = loggerFactory;
    }

    // 注入服務到容器中
    public void ConfigureServices(IServiceCollection services)
    {
        var logger = _loggerFactory.CreateLogger<Startup>();

        if (_env.IsDevelopment())
        {
            // Development service configuration
            logger.LogInformation("Development environment");
        }
        else
        {
            // Non-development service configuration
            logger.LogInformation($"Environment: {_env.EnvironmentName}");
        }

        ...
    }

    // 配置Http請求處理管道
    public void Configure(IApplicationBuilder app)
    {
        ...
    }
}

  Startup 類的 執行順序:構造 -> ConfigureServices -> Configure

     1)Startup Constructor(構造函數)

  上面的構造函數引出了我們開篇說的三個重要對象:IHostingEnvironment ,IConfiguration ,ILoggerFactory ,這里先講構造函數的作用,這些對象后面會分篇講。顯而易見,這里主要是通過依賴注入實例化了該類中需要用到的對象(根據自己的業務),比較簡單。

  2) ConfigureServices

  首先這個方法是可選的,它的參數是IServiceCollection,這也是我們開篇說的重要對象,而且是非常重要的對象,這是一個原生的Ioc容器,所有需要用到的服務都可以注冊到里面,一般是通過約定風格services.Addxxx, 這樣就可以讓這些服務在應用和Configure方法使用(用來構建管道)。

  3)Configure

   用於構建管道處理Http請求,管道中的每個中間件(Middleware)組件負責請求處理和選擇是否將請求傳遞到管道中的下一個組件,在這里我們可以添加自己想要的中間件來處理每一個Http請求,一般是使用上面的ConfigureServices方法中注冊好的服務,一般的用法是 app.Usexxx,這個Usexxx方法是基於IApplicationBuilder的擴展。

   需要注意的有三個地方:

  1. 應盡早在管道中調用異常處理委托,這樣就能捕獲在后續管道發生的異常,所以能看到微軟的經典寫法是先把異常處理的中間件寫在最前面,這樣方可捕獲稍后調用中發生的任何異常。
  2. 當某個中間件不將請求傳遞給下一個中間件時,這被稱為“請求管道短路”。 我們通常都會需要短路,這樣可以避免資源浪費,類似與當拋出異常時我們將不會再往下請求,因為這完全沒有必要:)
  3. 如果你想某些模塊不需要授權就能訪問,應把這些模塊放在認證模塊前面,所以我們一般會把訪問靜態文件的中間件放在認證模塊的前面。
public void Configure(IApplicationBuilder app)
{
    if (env.IsDevelopment())
    {//   Use the Developer Exception Page to report app runtime errors.
        app.UseDeveloperExceptionPage();
    }
    else
    {//   Enable the Exception Handler Middleware to catch exceptions
        //     thrown in the following middlewares.
        app.UseExceptionHandler("/Error");
    }
// Return static files and end the pipeline.
    app.UseStaticFiles();

    // Use Cookie Policy Middleware to conform to EU General Data 
    // Protection Regulation (GDPR) regulations.
    app.UseCookiePolicy();

    // Authenticate before the user accesses secure resources.
    app.UseAuthentication();

    // If the app uses session state, call Session Middleware after Cookie 
    // Policy Middleware and before MVC Middleware.
    app.UseSession();

    // Add MVC to the request pipeline.
    app.UseMvc();
}

  如果你不想使用Startup類的話,可以使用以下方式配置自己的服務注冊和管道構建,雖然這種方式有點odd :)

public class Program
{
    public static IHostingEnvironment HostingEnvironment { get; set; }
    public static IConfiguration Configuration { get; set; }

    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
            })
            .ConfigureServices(services =>
            {
                ...
            })
            .Configure(app =>
            {
                var loggerFactory = app.ApplicationServices
                    .GetRequiredService<ILoggerFactory>();
                var logger = loggerFactory.CreateLogger<Program>();
                var env = app.ApplicationServices.GetRequiredServices<IHostingEnvironment>();
                var config = app.ApplicationServices.GetRequiredServices<IConfiguration>();

                logger.LogInformation("Logged in Configure");

                if (env.IsDevelopment())
                {
                    ...
                }
                else
                {
                    ...
                }

                var configValue = config["subsection:suboption1"];

                ...
            });
}

   這里需要注意的是,Startup只是一個概念,類的名字是可以任意的,只需要在啟動項UseStartup中指定你這個啟動類即可。

  總結

  正如ASP.NET Core - 開篇所說的,一個ASP.NET Core應用其實就是一個控制台應用程序,它在應用啟動時構建一個 Web 服務器,並且通過指定的Startup類來構建應用服務和請求管道,進而監聽和處理所有的Http請求。


免責聲明!

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



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