TL;DR
本文为面试遇到启动顺序问题,先上结论,感兴趣可以往下看(会简单聊下源码,对比下 .NET Core 各个版本的区别)
ConfigureWebHostDefaults
ConfigureHostConfiguration
ConfigureAppConfiguration
以下根据语句的先后顺序执行
Startup.ConfigureServices
ConfigureLogging
ConfigureServices`
Startup.Configure
结果是很容易得出,dotnet new webapi, 然后对应位置console下就好了,但是为什么如此呢? 如果根据结果去推原因,并不是一个好的方法,你会用各种不知对错的想法去对应结果(不只是代码,人生也是如果 就如各种畅销书/垃圾水文,根据人的成功,去证明人的各种小习惯的重要性)。所以本文试图通过两个方式探究下原因。
Program, Startup 变化 .NET Core 1.0 Program.cs 1 2 3 4 5 6 7 8 9 10 11 12 public static void Main (string [] args ) { var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>() .UseApplicationInsights() .Build(); host.Run(); }
Startup.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public Startup (IHostingEnvironment env ) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json" , optional: false , reloadOnChange: true ) .AddJsonFile($"appsettings.{env.EnvironmentName} .json" , optional: true ) .AddEnvironmentVariables(); Configuration = builder.Build(); }public IConfigurationRoot Configuration { get ; }public void ConfigureServices (IServiceCollection services ) { services.AddMvc(); }public void Configure (IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory ) { loggerFactory.AddConsole(Configuration.GetSection("Logging" )); loggerFactory.AddDebug(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(); }
.NET Core 1.1 Program.cs 与 1.0
一致
Startup.cs 与 1.0
一致
.NET Core 2.0 Program.cs 1 2 3 4 5 6 7 8 9 10 11 12 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.0的时候已经清爽了不少,把那些默认的中间件的使用都放在了 CreateDefaultBuilder
中,我们来看下这个方法做了哪些事情(代码在下边),可以看到UseKestrel,UseIIS都是在这个方法中实现的。并且把 .NET Core 1 中Startup做的config部分工作放在了这里,还有log部分,也是在此实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static IWebHostBuilder CreateDefaultBuilder (string [] args ) { return new WebHostBuilder().UseKestrel().UseContentRoot(Directory.GetCurrentDirectory()).ConfigureAppConfiguration(delegate (WebHostBuilderContext hostingContext, IConfigurationBuilder config) { IHostingEnvironment hostingEnvironment = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json" , optional: true , reloadOnChange: true ).AddJsonFile($"appsettings.{hostingEnvironment.EnvironmentName} .json" , optional: true , reloadOnChange: true ); if (hostingEnvironment.IsDevelopment()) { Assembly assembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName)); if (assembly != null ) { config.AddUserSecrets(assembly, optional: true ); } } config.AddEnvironmentVariables(); if (args != null ) { config.AddCommandLine(args); } }) .ConfigureLogging(delegate (WebHostBuilderContext hostingContext, ILoggingBuilder logging) { logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging" )); logging.AddConsole(); logging.AddDebug(); }) .UseIISIntegration() .UseDefaultServiceProvider(delegate (WebHostBuilderContext context, ServiceProviderOptions options) { options.ValidateScopes = context.HostingEnvironment.IsDevelopment(); }); }
Startup.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Startup { public Startup (IConfiguration configuration ) { Configuration = configuration; } public IConfiguration Configuration { get ; } public void ConfigureServices (IServiceCollection services ) { services.AddMvc(); } public void Configure (IApplicationBuilder app, IHostingEnvironment env ) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(); } }
因为在main函数中就把config配置好了,所以Startup的ctor参数也从 IHostingEnvironment
-> IConfiguration
, 从这之后基本就没有太大的变动了
BTW 常看报文的肯定经常看到这个 Server: Kestrel
, 有心的人估计也查过这个,知道这个是与 Nginx,IIS,Apache一样的,用来负载你的web程序,但是有一大部分人都说自己在程序中并没用用过这个,部署的时候也一直是 nignx,IIS, 实际上是微软已经默认使用了。
.NET Core 2.1 Program.cs 1 2 3 4 5 6 7 8 9 10 11 public class Program { public static void Main (string [] args ) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder (string [] args ) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); }
换了下函数的返回值 IWebHost
-> IWebHostBuilder
Startup.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Startup { public Startup (IConfiguration configuration ) { Configuration = configuration; } public IConfiguration Configuration { get ; } public void ConfigureServices (IServiceCollection services ) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); } public void Configure (IApplicationBuilder app, IHostingEnvironment env ) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseHsts(); } app.UseHttpsRedirection(); app.UseMvc(); } }
SetCompatibilityVersion
看名字也知道是为了兼容性,不用管 多了一个HSTS 的中间件(为了强制使用HTTPS),估计是 RFC 有啥新出条例,所以项目默认支持UseHttpsRedirection
一样是为了安全
这一版基本没什么变化
.NET Core 2.2 Program.cs 无变化
Startup.cs 基本无变化
.NET Core 3.1 Program.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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>(); }); }
IWebHostBuilder
-> IHostBuilder
这样可以更好的将 ASP.NET Core 应用与非 Web 特定的其他服务器方案集成 ,通过 Hosting Exception, create default build, config web host default 启用 API 。
这一部分更多是,微软对于 .NET Core 代码类库的变化,整体的封装都做了很大的调整(v2 的时候出了Microsoft.AspNetCore.All
,基本引入的所有的必需包,当然会导致用不到的包也引入了,项目加载必然会慢一点,V3的时候做了调整 )
Startup.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class Startup { public Startup (IConfiguration configuration ) { Configuration = configuration; } public IConfiguration Configuration { get ; } public void ConfigureServices (IServiceCollection services ) { services.AddControllers(); } public void Configure (IApplicationBuilder app, IWebHostEnvironment env ) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } }
这次是将 AddMvc -> AddControllers, 即默认的注入变为Controll,不再包括视图(view,page),也是因为这个变化,可以看到下边多了一些中间件的注入(route,authorization,endpoint)。
为什么这个时候多出了这些呢? 如果看下之前代码,会发现的UseMvc()中route,authorization,endpoint这些东西实际上在MVC中都有实现。
可以说这次的更新,微软更多的是把默认配置变得更轻巧,灵活一点。(当然之前的useMvc也是可以正常用的,注意 微软只是改的默认写法,而不是规定你必须这样做 )
.NET Core 5 Program.cs 无变化
Startup.cs 无变化
PS. 从这一代,webapi项目默认集成了OpenAPI(Swagger)
.NET Core 6 Program.cs 无变化
Startup.cs 无变化
瞧瞧源码 先把相关的方法都加上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Program { public static void Main (string [] args ) => CreateHostBuilder(args).Build().Run(); public static IHostBuilder CreateHostBuilder (string [] args ) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration(builder => { Console.WriteLine("ConfigureAppConfiguration" ); }) .ConfigureWebHostDefaults(webBuilder => { Console.WriteLine(" ConfigureWebHostDefaults" ); webBuilder.UseStartup<Startup>(); }) .ConfigureLogging(log => { Console.WriteLine("ConfigureLogging" ); }) .ConfigureServices(service => { Console.WriteLine("ConfigureServices" ); }) .ConfigureHostConfiguration(config => { Console.WriteLine("ConfigureHostConfiguration" ); }); }
ConfigureWebHostDefaults
ConfigureHostConfiguration
ConfigureAppConfiguration
Startup
Startup.ConfigureServices
ConfigureLogging
ConfigureServices
service:3226198
service2:29035785
Startup.Configure