ASP.NET Core (Hosting)

Feng Gao
7 min readJul 24, 2021

--

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 应用程序

  1. Host.CreateDefaultBuilder 方法创建一个 IHostBuilder 的对象
  2. ConfigureWebHostDefaultsIHostBuilder 的拓展方法,主要是调用 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 中的依赖注入服务, 包括 IWebHostEnvironmentIApplicationLifeTime , 添加 GenericWebHostSeriveOptions Option 选项,添加 DiagnosticListenerDiagnosticSource ; IHttpContextFactoryDefaultHttpContextFactory 提供服务; IMiddlewareFactory 服务由 MiddlewareFactory 服务提供; IApplicationBuilderFactoryApplicationBuilderFactory 服务提供
  • 如果选项中添加了 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 对象,然后调用 ServerStartAsync 方法来启动整个服务。

--

--

Feng Gao
Feng Gao

Written by Feng Gao

A software developer in Microsoft at Suzhou. Most articles spoken language is Chinese. I will try with English when I’m ready

No responses yet