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做了什么工作呢,我们下篇再来讨论。