本文不是针对初学者的入门文章,而是从asp.net core 5.0 源码的角度分析程序是如何加载并运行的。
创建 ASP.NET Core Web App(MVC) 模板项目
运行 VS2019 并创建新的ASP.NET Core Web App(Model-View-Controller) 模板项目, 在创建向导中 Target Framework选择 .Net 5.0,我们暂时不需要任何的身份验证 所以 Authentication Type
选择 None,项目创建完成后目录结构类似于:
直接 F5 运行,浏览器会默认打开 http://localhost:5000/ 的地址,模板只含有两个页面 Home 及 Privacy
分析模板代码
下面来分析主程序,打开Program.cs
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>();
});
}
CreateHostBuilder 方法会创建并配置一个 builder 对象,然后调用Build方法创建一个IHost的实例,最后调用Run 方法启动host。
下面一步步分析这段代码。
首先Host是一个静态类,命名空间是Microsoft.Extensions.Hosting, 这个静态类中只包含两个静态方法,还是重载方法, 只参数不同
public static IHostBuilder CreateDefaultBuilder();
public static IHostBuilder CreateDefaultBuilder(string[] args);
来看看CreateDefaultBuilder具体的实现,去掉了一些具体的配置代码
public static IHostBuilder CreateDefaultBuilder(string[] args)
{
var builder = new HostBuilder();
builder.UseContentRoot(Directory.GetCurrentDirectory());
builder.ConfigureHostConfiguration(config =>
{
//加载DOTNET_ 环境变量和命令行参数
});
builder.ConfigureAppConfiguration((hostingContext, config) =>
{
//添加json配置文件, user secret,环境变量和命令行参数
})
.ConfigureLogging((hostingContext, logging) =>
{
//添加log:Event log,Console log,Debug, Event source,配置文件的log
})
.UseDefaultServiceProvider((context, options) =>
{
//配置依赖注入服务的验证信息
});
return builder;
}
通过调用此方法就初始化好了一个IHostBuilder的实例(HostBuilder)
,至此所有配置的加载以及代码都是针对通用宿主(generic host),没有针对asp.net core 或 HTTP 请求的任何代码,在这个通用宿主的基础上我们可以设置成控制台程序,worker service,以及asp.net core 应用。
要把程序设成asp.net core, 程序调用IHostBuilder的一个扩展方法ConfigureWebHostDefaults,这个方法位于静态类GenericHostBuilderExtensions 中, 调用此方法是为hosting web app加载一些默认的选项。
public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure)
{
if (configure is null)
{
throw new ArgumentNullException(nameof(configure));
}
return builder.ConfigureWebHost(webHostBuilder =>
{
WebHost.ConfigureWebDefaults(webHostBuilder);
configure(webHostBuilder);
});
}
此方法中继续调用IHostBuilder的另外一个扩展方法ConfigureWebHost
public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action<IWebHostBuilder> configure, Action<WebHostBuilderOptions> configureWebHostBuilder)
{
//省略一些参数的检查代码
// Light up custom implementations namely ConfigureHostBuilder which throws.
if (builder is ISupportsConfigureWebHost supportsConfigureWebHost)
{
return supportsConfigureWebHost.ConfigureWebHost(configure, configureWebHostBuilder);
}
var webHostBuilderOptions = new WebHostBuilderOptions();
configureWebHostBuilder(webHostBuilderOptions);
var webhostBuilder = new GenericWebHostBuilder(builder, webHostBuilderOptions);
configure(webhostBuilder);
builder.ConfigureServices((context, services) => services.AddHostedService<GenericWebHostService>());
return builder;
}
在ConfigureWebHost中,首先创建一个GenericWebHostBuilder
的对象,然后调用的传入的委托方法,在上一个ConfigureWebHostDefaults方法中,传入的委托方法是
webHostBuilder =>
{
WebHost.ConfigureWebDefaults(webHostBuilder);
configure(webHostBuilder);
}
委托方法先是调用的静态类WebHost
)
中的ConfigureWebDefaults,然后接着调用上一级传入的委托方法及Program.cs中传入的
webBuilder =>
{
webBuilder.UseStartup<Startup>();
}
ConfigureWebHostDefaults
下面概括一下这个扩展方法
- 添加 ASPNETCORE_ 开头的环境变量
- 注册 GenericHostService(详见ConfigureWebHost方法), 这个类是IHostService的一个实现,这个类实现了让asp.net core 可以使用 generic host
- 配置 Kestrel 当作默认Http Server 服务,反向代理伺服器(例如IIS,Nginx,Apache)会把接受http请求,并转发给Kestrel
- 注册服务HostFilteringStartupFilter
- 在Windows上启动IIS 集成
- 注册路由服务
HostBuilder
这是一个非常重要的类,前面我们讲到了CreateDefaultBuilder是用来初始化一个IHostBuilder的实例,这个实例的默认实现就是HostBuilder,前面讲到的所有配置信息都是应用到这个类的实例的,这个类里定义了几个委托的list集合
private List<Action<IConfigurationBuilder>> _configureHostConfigActions = new List<Action<IConfigurationBuilder>>();
private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions = new List<Action<HostBuilderContext, IConfigurationBuilder>>();
private List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions = new List<Action<HostBuilderContext, IServiceCollection>>();
private List<IConfigureContainerAdapter> _configureContainerActions = new List<IConfigureContainerAdapter>();
前面我们看到的几乎所有的加载设置,服务之类的都是添加到对应的委托集合中,然后这些委托会在后续的Build方法中被循环调用。
总结
我们今天创建了一个asp.net core 5.0 mvc模板项目,并解释了CreateHostBuilder做的几件事情
- 创建一个IHostBuilder的实例并加载一些针对generic host的设置,环境变量的添加、json配置文件加载、log等
- 接着调用ConfigureWebHostDefaults并宿主配置成能asp.net core的程序
接下来的一篇文章我会解释一下Build这个方法。