欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

理解ASP.NET Core 啟動類(Startup)

 更新時間:2021年09月02日 11:58:38   作者:xiaoxiaotank  
這篇文章主要介紹了ASP.NET Core 啟動類(Startup),文中運用代碼講解相關知識非常詳細,感興趣的小伙伴可以參考一下

準備工作:一份ASP.NET Core Web API應用程序

當我們來到一個陌生的環(huán)境,第一件事就是找到廁所在哪。

當我們接觸一份新框架時,第一件事就是找到程序入口,即Main方法

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

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

代碼很簡單,典型的建造者模式:通過IHostBuilder創(chuàng)建一個通用主機(Generic Host),然后啟動它(至于什么是通用主機,咱們后續(xù)的文章會說到)。咱們不要一上來就去研究CreateDefaultBuilder、ConfigureWebHostDefaults這些方法的源代碼,應該去尋找能看的見、摸得著的,很明顯,只有Startup。

Startup類

Startup類承擔應用的啟動任務,所以按照約定,起名為Startup,不過你可以修改為任意類名(強烈建議類名為Startup)。

默認的Startup結構很簡單,包含:

  • 構造函數(shù)
  • Configuration屬性
  • ConfigureServices方法
  • Configure方法
public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    // 該方法由運行時調用,使用該方法向DI容器添加服務
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    // 該方法由運行時調用,使用該方法配置HTTP請求管道
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
    }
}

Startup構造函數(shù)

當使用通用主機(Generic Host)時,Startup構造函數(shù)支持注入以下三種服務類型:

  • IConfiguration
  • IWebHostEnvironment
  • IHostEnvironment
public Startup(
    IConfiguration configuration,
    IHostEnvironment hostEnvironment,
    IWebHostEnvironment webHostEnvironment)
{
    Configuration = configuration;
    HostEnvironment = hostEnvironment;
    WebHostEnvironment = webHostEnvironment;
}

public IConfiguration Configuration { get; }

public IHostEnvironment HostEnvironment { get; set; }

public IWebHostEnvironment WebHostEnvironment { get; set; }

這里你會發(fā)現(xiàn) HostEnvironmentWebHostEnvironment 的實例是同一個。別著急,后續(xù)文章我們聊到Host的時候,你就明白了。

ConfigureServices

  • 該方法是可選的
  • 該方法用于添加服務到DI容器中
  • 該方法在Configure方法之前被調用
  • 該方法要么無參數(shù),要么只能有一個參數(shù)且類型必須為IServiceCollection
  • 該方法內的代碼大多是形如Add{Service}的擴展方法

常用的服務有(部分服務框架已默認注冊):

  • AddControllers:注冊Controller相關服務,內部調用了AddMvcCore、AddApiExplorer、AddAuthorizationAddCors、AddDataAnnotations、AddFormatterMappings等多個擴展方法
  • AddOptions:注冊Options相關服務,如IOptions<>、IOptionsSnapshot<>、IOptionsMonitor<>、IOptionsFactory<>IOptionsMonitorCache<>等。很多服務都需要Options,所以很多服務注冊的擴展方法會在內部調用AddOptions
  • AddRouting:注冊路由相關服務,如IInlineConstraintResolverLinkGenerator、IConfigureOptions<RouteOptions>RoutePatternTransformer
  • AddAddLogging:注冊Logging相關服務,如ILoggerFactory、ILogger<>IConfigureOptions<LoggerFilterOptions>>
  • AddAuthentication:注冊身份認證相關服務,以方便后續(xù)注冊JwtBearer、Cookie等服務
  • AddAuthorization:注冊用戶授權相關服務
  • AddMvc:注冊Mvc相關服務,比如Controllers、Views、RazorPages等
  • AddHealthChecks:注冊健康檢查相關服務,如HealthCheckService、IHostedService

Configure

  • 該方法是必須的
  • 該方法用于配置HTTP請求管道,通過向管道添加中間件,應用不同的響應方式。
  • 該方法在ConfigureServices方法之后被調用
  • 該方法中的參數(shù)可以接受任何已注入到DI容器中的服務
  • 該方法內的代碼大多是形如Use{Middleware}的擴展方法
  • 該方法內中間件的注冊順序與代碼的書寫順序是一致的,先注冊的先執(zhí)行,后注冊的后執(zhí)行

常用的中間件有

  • UseDeveloperExceptionPage:當發(fā)生異常時,展示開發(fā)人員異常信息頁。如圖

  • UseRouting:路由中間件,根據(jù)Url中的路徑導航到對應的Endpoint。必須與UseEndpoints搭配使用。
  • UseEndpoints:執(zhí)行路由所選擇的Endpoint對應的委托。
  • UseAuthentication:身份認證中間件,用于對請求用戶的身份進行認證。比如,早晨上班打卡時,管理員認出你是公司員工,那么才允許你進入公司。
  • UseAuthorization:用戶授權中間件,用于對請求用戶進行授權。比如,雖然你是公司員工,但是你是一名.NET開發(fā)工程師,那么你只允許坐在.NET開發(fā)工程師區(qū)域的工位上,而不能坐在老總的辦公室里。
  • UseMvc:Mvc中間件。
  • UseHealthChecks:健康檢查中間件。
  • UseMiddleware:用來添加匿名中間件的,通過該方法,可以方便的添加自定義中間件。

省略Startup類

另外,Startup類也可以省略,直接進行如下配置即可(雖然可以這樣做,但是不推薦):

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            // ConfigureServices 可以調用多次,最終會將結果聚合
            webBuilder.ConfigureServices(services =>
            {
            })
            // Configure 如果調用多次,則只有最后一次生效
            .Configure(app =>
            {
                var env = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>();
            });
        });

IStartupFilter

public interface IStartupFilter
{
    Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next);
}

有時,我們想要將一系列相關中間件的注冊封裝到一起,那么我們只需要通過實現(xiàn)IStartupFilter,并在Startup.ConfigureServices中配置IStartupFilter的依賴注入即可。

  • IStartupFilter中配置的中間件,總是比Startup類中Configure方法中的中間件先注冊;對于多個IStartupFilter實現(xiàn),執(zhí)行順序與服務注冊時的順序一致

我們可以通過一個例子來驗證一下中間件的注冊順序。

首先是三個IStartupFilter的實現(xiàn)類:

public class FirstStartupFilter : IStartupFilter
{
    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    => app =>
    {
        app.Use((context, next) =>
        {
            Console.WriteLine("First");
            return next();
        });
        next(app);
    };
}

public class SecondStartupFilter : IStartupFilter
{
    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    => app =>
    {
        app.Use((context, next) =>
        {
            Console.WriteLine("Second");
            return next();
        });
        next(app);
    };
}

public class ThirdStartupFilter : IStartupFilter
{
    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    => app =>
    {
        app.Use((context, next) =>
        {
            Console.WriteLine("Third");
            return next();
        });
        next(app);
    };
}

接下來進行注冊:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices(services =>
        {
            // 第一個被注冊
            services.AddTransient<IStartupFilter, FirstStartupFilter>();
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        })
        .ConfigureServices(services => 
        {
            // 第三個被注冊
            services.AddTransient<IStartupFilter, ThirdStartupFilter>();
        });
        
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // 第二個被注冊
        services.AddTransient<IStartupFilter, SecondStartupFilter>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // 第四個被注冊
        app.Use((context, next) =>
        {
            Console.WriteLine("Forth");
            return next();
        });
    }
}

最后通過輸出可以看到,執(zhí)行順序的確是這樣子的。

First
Second
Third
Forth

IHostingStartup

IStartupFilter不同的是,IHostingStartup可以在啟動時通過外部程序集向應用增加更多功能。不過這要求必須調用ConfigureWebHost、ConfigureWebHostDefaults等類似用來配置Web主機的擴展方法

我們經(jīng)常使用的Nuget包SkyApm.Agent.AspNetCore就使用了該特性。

下面我們就來看一下該如何使用它。

HostingStartup 程序集

要創(chuàng)建HostingStartup程序集,可以通過創(chuàng)建類庫項目或無入口點的控制臺應用來實現(xiàn)。

接下來咱們還是看一下上面提到過的SkyApm.Agent.AspNetCore

using SkyApm.Agent.AspNetCore;

[assembly: HostingStartup(typeof(SkyApmHostingStartup))]

namespace SkyApm.Agent.AspNetCore
{
    internal class SkyApmHostingStartup : IHostingStartup
    {
        public void Configure(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services => services.AddSkyAPM(ext => ext.AddAspNetCoreHosting()));
        }
    }
}

該HostingStartup類:

  • 實現(xiàn)了IHostingStartup接口
  • Configure方法中使用IWebHostBuilder來添加增強功能
  • 配置了HostingStartup特性

HostingStartup 特性

HostingStartup特性用于標識哪個類是HostingStartup類,HostingStartup類需要實現(xiàn)IHostingStartup接口。

當程序啟動時,會自動掃描入口程序集和配置的待激活的的程序集列表(參見下方:激活HostingStarup程序集),來找到所有的HostingStartup特性,并通過反射的方式創(chuàng)建Startup并調用Configure方法。

SkyApm.Agent.AspNetCore為例

using SkyApm.Agent.AspNetCore;

[assembly: HostingStartup(typeof(SkyApmHostingStartup))]

namespace SkyApm.Agent.AspNetCore
{
    internal class SkyApmHostingStartup : IHostingStartup
    {
        public void Configure(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services => services.AddSkyAPM(ext => ext.AddAspNetCoreHosting()));
        }
    }
}

激活HostingStarup程序集

要激活HostingStarup程序集,我們有兩種配置方式:

1.使用環(huán)境變量(推薦)

使用環(huán)境變量,無需侵入程序代碼,所以我更推薦大家使用這種方式。

配置環(huán)境變量ASPNETCORE_HOSTINGSTARTUPASSEMBLIES,多個程序集使用分號(;)進行分隔,用于添加要激活的程序集。變量WebHostDefaults.HostingStartupAssembliesKey就是指代這個環(huán)境變量的Key。

另外,還有一個環(huán)境變量,叫做ASPNETCORE_HOSTINGSTARTUPEXCLUDEASSEMBLIES,多個程序集使用分號(;)進行分隔,用于排除要激活的程序集。變量WebHostDefaults.HostingStartupExcludeAssembliesKey就是指代這個環(huán)境變量的Key。

我們在 launchSettings.json 中添加兩個程序集:

"environmentVariables": {
    "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "SkyAPM.Agent.AspNetCore;HostingStartupLibrary"
}

2.在程序中配置

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseSetting(
                WebHostDefaults.HostingStartupAssembliesKey,
                "SkyAPM.Agent.AspNetCore;HostingStartupLibrary")
            .UseStartup<Startup>();
        });

這樣就配置完成了,很🐮🍺的一個功能點吧!

需要注意的是,無論使用哪種配置方式,當存在多個HostingStartup程序集時,將按配置這些程序集時的書寫順序執(zhí)行 Configure方法。

多環(huán)境配置

一款軟件,一般要經(jīng)過需求分析、設計編碼,單元測試、集成測試以及系統(tǒng)測試等一系列測試流程,驗收,最終上線。那么,就至少需要4套環(huán)境來保證系統(tǒng)運行:

  • Development:開發(fā)環(huán)境,用于開發(fā)人員在本地對應用進行調試運行
  • Test:測試環(huán)境,用于測試人員對應用進行測試
  • Staging:預發(fā)布環(huán)境,用于在正式上線之前,對應用進行集成、測試和預覽,或用于驗收
  • Production:生產環(huán)境,應用的正式線上環(huán)境

環(huán)境配置方式

通過環(huán)境變量ASPNETCORE_ENVIRONMENT指定運行環(huán)境

注意:如果未指定環(huán)境,默認情況下,為 Production

在項目的Properties文件夾里面,有一個“l(fā)aunchSettings.json”文件,該文件是用于配置VS中項目啟動的。
接下來我們就在launchSettings.json中配置一下。
先解釋一下該文件中出現(xiàn)的幾個參數(shù):

  • commandName:指定要啟動的Web服務器,有三個可選值:

Project:啟動 Kestrel

IISExpress:啟動IIS Express

IIS:不啟用任何Web服務器,使用IIS

  • dotnetRunMessages:bool字符串,指示當使用 dotnet run 命令時,終端能夠及時響應并輸出消息,具體參考stackoverflowgithub issue
  • launchBrowser:bool值,指示當程序啟動后,是否打開瀏覽器
  • launchUrl:默認啟動路徑
  • applicationUrl:應用程序Url列表,多個URL之間使用分號(;)進行分隔。當launchBrowser為true時,將{applicationUrl}/{launchUrl}作為瀏覽器默認訪問的Url
  • environmentVariables:環(huán)境變量集合,在該集合內配置環(huán)境變量
{
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "profiles": {
    // 如果不指定profile,則默認選擇第一個
    // Development
    "ASP.NET.WebAPI": {
      "commandName": "Project",
      "dotnetRunMessages": "true",
      "launchBrowser": true,
      "launchUrl": "weatherforecast",
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    // Test
    "ASP.NET.WebAPI.Test": {
      "commandName": "Project",
      "dotnetRunMessages": "true",
      "launchBrowser": true,
      "launchUrl": "weatherforecast",
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Test"
      }
    },
    // Staging
    "ASP.NET.WebAPI.Staging": {
      "commandName": "Project",
      "dotnetRunMessages": "true",
      "launchBrowser": true,
      "launchUrl": "weatherforecast",
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Staging"
      }
    },
    // Production
    "ASP.NET.WebAPI.Production": {
      "commandName": "Project",
      "dotnetRunMessages": "true",
      "launchBrowser": true,
      "launchUrl": "weatherforecast",
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Production"
      }
    },
    // 用于測試在未指定環(huán)境時,默認是否為Production
    "ASP.NET.WebAPI.Default": {
      "commandName": "Project",
      "dotnetRunMessages": "true",
      "launchBrowser": true,
      "launchUrl": "weatherforecast",
      "applicationUrl": "http://localhost:5000"
    }
  }
}

配置完成后,就可以在VS上方工具欄中的項目啟動處選擇啟動項了

基于環(huán)境的 Startup

Startup類支持針對不同環(huán)境進行個性化配置,有三種方式:

  • 1.將IWebHostEnvironment注入 Startup 類
  • 2.Startup 方法約定
  • 3.Startup 類約定

1.將IWebHostEnvironment注入 Startup 類

通過將IWebHostEnvironment注入 Startup 類,然后在方法中使用條件判斷書寫不同環(huán)境下的代碼。該方式適用于多環(huán)境下,代碼差異較少的情況。

public class Startup
{
    public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
    {
        Configuration = configuration;
        WebHostEnvironment = webHostEnvironment;
    }

    public IConfiguration Configuration { get; }

    public IWebHostEnvironment WebHostEnvironment { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        if (WebHostEnvironment.IsDevelopment())
        {
            Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}");
        }
        else if (WebHostEnvironment.IsTest())
        {
            Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}");
        }
        else if (WebHostEnvironment.IsStaging())
        {
            Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}");
        }
        else if (WebHostEnvironment.IsProduction())
        {
            Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}");
        }
    }

    public void Configure(IApplicationBuilder app)
    {
        if (WebHostEnvironment.IsDevelopment())
        {
            Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}");
        }
        else if (WebHostEnvironment.IsTest())
        {
            Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}");
        }
        else if (WebHostEnvironment.IsStaging())
        {
            Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}");
        }
        else if (WebHostEnvironment.IsProduction())
        {
            Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}");
        }
    }
}

public static class AppHostEnvironmentEnvExtensions
{
    public static bool IsTest(this IHostEnvironment hostEnvironment)
    {
        if (hostEnvironment == null)
        {
            throw new ArgumentNullException(nameof(hostEnvironment));
        }

        return hostEnvironment.IsEnvironment(AppEnvironments.Test);
    }
}

public static class AppEnvironments
{
    public static readonly string Test = nameof(Test);
}

2.Startup 方法約定

上面的方式把不同環(huán)境的代碼放在了同一個方法中,看起來比較混亂也不容易區(qū)分。因此我們希望ConfigureServicesConfigure能夠根據(jù)不同的環(huán)境進行代碼拆分。

我們可以通過方法命名約定來解決,約定Configure{EnvironmentName}ServicesConfigure{EnvironmentName}Services來裝載不同環(huán)境的代碼。如果當前環(huán)境沒有對應的方法,則使用原來的ConfigureServicesConfigure方法。

我就只拿 Development 和 Production 舉例了

public class Startup
{
    // 我這里注入 IWebHostEnvironment,僅僅是為了打印出來當前環(huán)境信息
    public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
    {
        Configuration = configuration;
        WebHostEnvironment = webHostEnvironment;
    }

    public IConfiguration Configuration { get; }

    public IWebHostEnvironment WebHostEnvironment { get; }

    #region ConfigureServices
    private void StartupConfigureServices(IServiceCollection services)
    {
        Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}");
    }

    public void ConfigureDevelopmentServices(IServiceCollection services)
    {
        StartupConfigureServices(services);
    }

    public void ConfigureProductionServices(IServiceCollection services)
    {
        StartupConfigureServices(services);
    }

    public void ConfigureServices(IServiceCollection services)
    {
        StartupConfigureServices(services);
    }
    #endregion

    #region Configure
    private void StartupConfigure(IApplicationBuilder app)
    {
        Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}");
    }

    public void ConfigureDevelopment(IApplicationBuilder app)
    {
        StartupConfigure(app);
    }

    public void ConfigureProduction(IApplicationBuilder app)
    {
        StartupConfigure(app);
    }

    public void Configure(IApplicationBuilder app)
    {
        StartupConfigure(app);
    } 
    #endregion
}

3.Startup 類約定

該方式適用于多環(huán)境下,代碼差異較大的情況。

程序啟動時,會優(yōu)先尋找當前環(huán)境命名符合Startup{EnvironmentName}的 Startup 類,如果找不到,則使用名稱為Startup的類

首先,CreateHostBuilder方法需要做一處修改

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            //webBuilder.UseStartup<Startup>();

            webBuilder.UseStartup(typeof(Startup).GetTypeInfo().Assembly.FullName);
        });

接下來,就是為各個環(huán)境定義 Startup 類了(我就只拿 Development 和 Production 舉例了)

public class StartupDevelopment
{
    // 我這里注入 IWebHostEnvironment,僅僅是為了打印出來當前環(huán)境信息
    public StartupDevelopment(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
    {
        Configuration = configuration;
        WebHostEnvironment = webHostEnvironment;
    }

    public IConfiguration Configuration { get; }

    public IWebHostEnvironment WebHostEnvironment { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}");
    }

    public void Configure(IApplicationBuilder app)
    {
        Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}");
    }
}

public class StartupProduction
{
    public StartupProduction(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
    {
        Configuration = configuration;
        WebHostEnvironment = webHostEnvironment;
    }

    public IConfiguration Configuration { get; }

    public IWebHostEnvironment WebHostEnvironment { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        Console.WriteLine($"{nameof(ConfigureServices)}: {WebHostEnvironment.EnvironmentName}");
    }

    public void Configure(IApplicationBuilder app)
    {
        Console.WriteLine($"{nameof(Configure)}: {WebHostEnvironment.EnvironmentName}");
    }
}

到此這篇關于理解ASP.NET Core 啟動類(Startup)的文章就介紹到這了,更多相關ASP.NET Core Startup內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論