ASP.NET Core 是微軟推出的一種全新的跨平台開源 .NET 框架,用於在 Windows、Mac 或 Linux 上生成基於雲的新式 Web 應用程序。國內目前關於Asp.Net Core的書比較少,自己靠着閱讀微軟官方文檔,源碼和在52ABP梁老師的教程中慢慢的在一點點的積累Asp.Net Core的知識。因此希望將自己的學習記錄下來,以此鞏固和交流。
閑話不多說,我們知道學習一門新的技術,最好的方法就是從它的啟動入口進行跟蹤,那么接下來我們先來看下Asp.Net Core的入口文件Program.cs。
筆者使用的.Net SDK 是 2.2版本,所以下面的介紹都是基於2.2版本的。
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); }
我們看到Program中Main函數很像我們之前的Console應用程序,那么它當中的CreateWebHostBuilder究竟做了什么事情呢?既然.Net Core是開源的,我們看源碼就方便了很多,接下來就從源碼來了解下它究竟做了什么工作。
/// <summary> /// Initializes a new instance of the <see cref="WebHostBuilder"/> class with pre-configured defaults. /// </summary> /// <remarks> /// The following defaults are applied to the returned <see cref="WebHostBuilder"/>: /// use Kestrel as the web server and configure it using the application's configuration providers, /// set the <see cref="IHostingEnvironment.ContentRootPath"/> to the result of <see cref="Directory.GetCurrentDirectory()"/>, /// load <see cref="IConfiguration"/> from 'appsettings.json' and 'appsettings.[<see cref="IHostingEnvironment.EnvironmentName"/>].json', /// load <see cref="IConfiguration"/> from User Secrets when <see cref="IHostingEnvironment.EnvironmentName"/> is 'Development' using the entry assembly, /// load <see cref="IConfiguration"/> from environment variables, /// load <see cref="IConfiguration"/> from supplied command line args, /// configure the <see cref="ILoggerFactory"/> to log to the console and debug output, /// and enable IIS integration. /// </remarks> /// <param name="args">The command line args.</param> /// <returns>The initialized <see cref="IWebHostBuilder"/>.</returns> 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; }
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>(); services.AddRouting(); }) .UseIIS() .UseIISIntegration(); }
從源碼中我們可以看到,CreateDefaultBuilder執行的任務有:
1、加載主機和應用程序的配置表信息
2、配置日志記錄
3、設置Web服務器
4、設置Asp.Net Core應用程序的托管形式。
現在我們來看一下Asp.Net Core是如何加載配置的。
訪問配置信息可以通過 IConfiguration 接口進行訪問,我們從源碼也可以看到,Asp.Net Core加載配置的過程,它們之間是按照順序如果有相同的配置會依次被覆蓋:appsettings.json,appsettings{Environment}.json(應用程序配置文件) -->User Secrets(用戶機密) --> Environment Variables(環境變量) --> Command-line arguments(命令行參數)。
其中應用程序配置文件的{Environment}可以在項目的屬性中調試進行配置,如果項目中不存在單獨為某個環境配置的,采用appsettings.json。
應用程序配置文件我們比較清楚,以前在.Net Framework我們有web.config,但是Asp.Net Core中出現了User Secrets用戶機密,那么它有什么用呢,其實它可以保存我們比較敏感的配置信息,比如第三方的Key,像微信的Appkey之類的。那我們該如何添加User Secrets呢?我們需要在命令行的窗口下,切換到我們項目的執行文件夾下,即bin所在的那層目錄,運行命令dotnet user-secrets set [KeyName] [KeyValue]
接着我們就可以在VS項目中查看到該用戶機密了
Environment variables(環境變量)在 Properties文件夾下的launchSettings.json進行配置
{ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:4084", "sslPort": 44338 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "StudentManagement": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }
這里的profiles可以在VS調試運行的時候做選擇
而Command-line arguments(命令行參數)需要結合命令行窗口執行: dotnet run KeyName="KeyValue"。
那么我們怎么獲取配置信息呢? Asp.Net Core提供了ConfigureAppConfiguration 可以給我們調用獲取配置,在StartUp文件中可以使用 _configuration["KeyName"] 來獲取。
最后,我們再看一下Asp.Net Core應用程序的托管形式,它有兩種托管形式:進程內托管InProcess和進程外托管OutOfProcess。我們知道Asp.Net Core是可以自托管的,它默認托管形式就是InProcess。那么這兩種方式的區別是什么呢?
InProcess:配置進程內托管在項目.csproj文件中 <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>,在InProcess托管情況下,CreateDefaultBuilder()方法調用UseIIS()方法並在IIS工作進程(w3wp.exe或iisexpress.exe)內托管應用程序,從性能角度,InProcess托管比OutOfProcess托管提供了更高的請求吞吐量。
OutOfProcess:有2個Web服務器-內部Web服務器和外部Web服務器,內部Web服務器是Kestrel,托管進程是dotnet.exe;外部web服務器可以是iis,nginx,apache。
現在我們知道了CreateDefaultBuilder做了什么工作了,那么在它之后調用了UseStartup<Startup>(),那Startup做了什么工作呢,我們下篇再來討論。