ASP.NET Core 应用本质上就是长时间运行的服务,该服务接受一个 HTTP 请求,然后按照预先设计好的路由规则,返回对应的响应(response)。一个 ASP.NET Core 应用程序包含两个主要的组件。
- Server (接受和响应请求)
- Middleware (处理请求的中间件)
在这篇文章中,将会通过阅读 ASP.NET Core 源码的方式,来探索 ASP.NET Core 如何将服务注册到 Host 中的。
1. Overview
Microsoft.Extensions.Hosting
规范了 .NET 平台下所有长时间运行的服务实现方法,具体的流程是这样的:
IHostBuilder
通过建造者模式创建一个 IHost
对象,在执行 Run
方法的时候,会从依赖注册容器中获取所有的 IHostedService
服务,然后依次启动它们。在 ASP.NET Core
框架中,我们将会把 GenericWebHostService
注册到容器中。
下面就是最简单的 ASP.NET Core 应用程序
Host.CreateDefaultBuilder
方法创建一个IHostBuilder
的对象ConfigureWebHostDefaults
是IHostBuilder
的拓展方法,主要是调用ConfigureWebHost
方法,其中调用了WebHost
的静态方法ConfigureWebDefaults
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);
});
}
3. WebHost.ConfigureWebDefaults
开始往服务注入相关服务
4. ConfigWebHost
开始注册 IHostedSerivce 对象 GenericWebHostService
5. Run
方法通过 IApplicationBuilder
注册了一个中间件
2. GenericWebHostService
通过上面的分析,我们可以知道,GenericWebHostService 就是最终长时间运行的服务, 那么我么来看一下这个服务依赖那些服务
3. GenericWebHostBuilder
GenericWebHostBuilder
是之前提供的一套框架,因为现在有了 IWebHostBuilder
之后,我们想要统一整个框架。但是之前的 IWebHostBuilder
仍然复用服务注册的方法。
那么 GenericWebHostBuilder
的初始化函数都做了哪些事情呢?
3.1 构造函数
- 创建一个
ConfiguraitonBuilder
对象,并且增加了一个内存源,尝试从环境变量中读取所有以ASPNETCORE_
开头的环境变量,并构建一个IConfiguraion
对象。 - 使用这个对象构建 Host 的配置信息
- 配置Host Application 的配置
- 接下来是往
builder
中的依赖注入服务, 包括IWebHostEnvironment
,IApplicationLifeTime
, 添加GenericWebHostSeriveOptions
Option 选项,添加DiagnosticListener
和DiagnosticSource
;IHttpContextFactory
由DefaultHttpContextFactory
提供服务;IMiddlewareFactory
服务由MiddlewareFactory
服务提供;IApplicationBuilderFactory
由ApplicationBuilderFactory
服务提供 - 如果选项中添加了 Startup 的的程序集,需要加载进来。
3.2 ConfigureAppConfiguration
该方法是对 IHostBuilder
接口的 ConfigureAppConfiguration
的封装。
3.3 ConfigureService
该方法也是对 IHostBuilder
接口的 ConfigServices
方法的封装,可以继续添加依赖的服务
3.4 UseStartup
我们也可以直接使用 UserStartup
注册服务和中间件,为了避免多次添加,我们只选择最后一次添加的 Startup 类
// UseStartup can be called multiple times. Only run the last one._startupObject = startupType;_builder.ConfigureServices((context, services) =>{// Run this delegate if the startup type matches
if (object.ReferenceEquals(_startupObject, startupType)){ UseStartup(startupType, context, services); }});return this;
那么真正调用的 UseStartup
方法都干了什么事情呢?
- 首选判断这个
Startup
这个类是否支持,如果类是实现IStartup
接口是不支持的,如果ConfigureService
方法返回IServiceProvider
也是不支持的, - 然后创建一个 Startup 的实例,但是要注意的是,它支持构造函数只包含
IHostingEnvironment,IWebHostEnvironment, IHostEnvironment, IConfiguration
这些参数的StartUp
类型 - 然后执行
ConfigServcie
这个方法,要注意这个方法名字可以是ConfigService
或者Config{Environment}Name
- 然后然后获取
Configure
方法,然后更新GenericWebHostServiceOptions
中的ConfiguApplication
方法,将Configure
方法中的注册的中间件添加到其中。
3.5 Configure 方法
除了使用 Startup
方法注册服务和中间件, Configure
方法也可以直接注册中间件,注意 Configure
方法和 UseStatup
方法最后一次的调用才会生效,该方法也继续修改 GenericWebHostSerivceOptions
中的 ConfigureApplication
属性。
4 GenericWebHostSerivce
在服务启动之后,注册在 IHost
中的 IHostedService
就会被执行,而 GenericWebHostService
就是被执行的 Asp.NET Core 服务。 那么我们来看看 StartAsync
方法是如何工作的
- 设置接受请求的地址,如果该服务可以配置请求地址,则从配置文件中,将地址添加到服务中。
- 在
options
中的请求获取所有注册的Action<IApplicationBuilder>
,也就是中间件,然后通过 ApplicationBuilderFactory 构建一个 IApplicaitonBuilder 对象,然后通过 build 方法,构建出一个RequestDelegate
对象 - 获取到对象后,创建一个 HostingApplication 对象,然后调用
Server
的StartAsync
方法来启动整个服务。