HostBuilder

很显然,HostBuildr 就是用来构建 Host 的构建器。

IHostBuilder 定义

通过 Build() 方法,构建器返回构建的 IHost 对象实例。

具体怎么构建呢?IHostBuilder 提供了多个扩展点,允许我们对构建过程进行扩展。

  • ConfigureHostConfiguration
  • ConfigureAppConfiguration
  • ConfigureServices
  • UseServiceProviderFactory
  • ConfigureContainer

对于应用配置:

  • ConfigureHostConfiguration
  • ConfigureAppConfiguration

对于容器管理:

  • 使用 ConfigureServices 方法添加服务注册,
  • 利用 UseServiceProviderFactory 方法注册 IServiceProviderFactory 工厂
  • 以及利用 ConfigureContainer 方法对依赖注入容器进行进一步的配置

IHostBuilder 接口定义,在 GitHub 查看 IHostBuilder 源码

namespace Microsoft.Extensions.Hosting
{
public interface IHostBuilder
{
IDictionary<object, object> Properties { get; }
IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate);
IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate);
IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate);
IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory);
IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory);
IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate); IHost Build();
}
}

默认的 HostBuilder 实现

IHostBuilder 的默认实现 是 HostBuilder,在 GitHub 中查看 HostBuilder 源码

针对扩展点,提供了默认的实现来供我们调用,以 ConfigureAppConfiguration 为例,

private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions
= new List<Action<HostBuilderContext, IConfigurationBuilder>>(); public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
{
_configureAppConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
return this;
}

可以看到内部提供了一个 Action 的集合,每次调用 ConfigureAppConfiguration() 方法,是将我们提供的 Action 追加到这个集合中。这意味着我们可以多次调用这个方法。

在源码中可以看到内部提供了多个集合。

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>>();

其中最重要的就是 Build 的实现了。

/// <summary>
/// Run the given actions to initialize the host. This can only be called once.
/// </summary>
/// <returns>An initialized <see cref="IHost"/></returns>
public IHost Build()
{
if (_hostBuilt)
{
throw new InvalidOperationException("Build can only be called once.");
}
_hostBuilt = true; BuildHostConfiguration();
CreateHostingEnvironment();
CreateHostBuilderContext();
BuildAppConfiguration();
CreateServiceProvider(); return _appServices.GetRequiredService<IHost>();
}

Build方法的内容也很简单,只有一个基本检查和六个步骤。

  1. 首先检查是否重复创建了IHost对象,每个IHostBuilder对象只能运行一次Build方法,否则抛出异常,确保我们不会重复创建IHost对象。
  2. 第二步初始化创建IHost对象过程中需要使用到的基本配置信息。
  3. 第三步初始化应用程序的Microsoft.Extensions.Hosting.IHostEnvironment对象,用来向应用程序提供程序运行环境信息(Development、Stage、Production等)。
  4. 第四步初始化(根据第二部初始化的基本配置信息)创建IHost对象所需要的所有上下文对象。
  5. 创建IOC容器,并将默认的IHost对象(Microsoft.Extensions.Hosting.Internal.Host)注入到IOC容易中。
  6. 然后将IHost对象从IOC容器里取出并返回给方法的调用方。

所以,对于 HostBuilder 来说,我们需要的就是通过这些扩展点来注册各用来配置这个 Builder 的 Action,它们会在 HostBuilder 的 Build() 方法被调用的时候,依此被调用执行,以完成最终 Host 的构建,最终构建出 Host 对象本身。

这里面的 CreateServiceProvider() 也值得一看。这里面构建了 ServiceCollection 对象实例,从此以后就可以使用依赖注入了。

private void CreateServiceProvider()
{
var services = new ServiceCollection();
#pragma warning disable CS0618 // Type or member is obsolete
services.AddSingleton<IHostingEnvironment>(_hostingEnvironment);
#pragma warning restore CS0618 // Type or member is obsolete
services.AddSingleton<IHostEnvironment>(_hostingEnvironment);
services.AddSingleton(_hostBuilderContext);
// register configuration as factory to make it dispose with the service provider
services.AddSingleton(_ => _appConfiguration);
#pragma warning disable CS0618 // Type or member is obsolete
services.AddSingleton<IApplicationLifetime>(s => (IApplicationLifetime)s.GetService<IHostApplicationLifetime>());
#pragma warning restore CS0618 // Type or member is obsolete
services.AddSingleton<IHostApplicationLifetime, ApplicationLifetime>();
services.AddSingleton<IHostLifetime, ConsoleLifetime>();
services.AddSingleton<IHost, Internal.Host>();
services.AddOptions();
services.AddLogging(); foreach (var configureServicesAction in _configureServicesActions)
{
configureServicesAction(_hostBuilderContext, services);
} var containerBuilder = _serviceProviderFactory.CreateBuilder(services); foreach (var containerAction in _configureContainerActions)
{
containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
} _appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder); if (_appServices == null)
{
throw new InvalidOperationException($"The IServiceProviderFactory returned a null IServiceProvider.");
} // resolve configuration explicitly once to mark it as resolved within the
// service provider, ensuring it will be properly disposed with the provider
_ = _appServices.GetService<IConfiguration>();
}

通过 CreateDefaultBuilder() 构建 HostBuilder 对象实例

那么,这个 HostBuilder 又是什么时候被创建出来的呢?回到我们的主程序,看一看 CreateHostBuilder() 这个方法。

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});

首先,这里使用了类 Host,并调用了它的静态方法 CreateDefaultBuilder()。注意这个 Host 类的命名空间是:Microsoft.Extensions.Hosting。而以前的 Host 所在的命名空间是 Microsoft.Extensions.Hosting.Internal。我们在 Program.cs 中看到的 Host 是这个静态类。

在 GitHub 中查看 Host 源码

namespace Microsoft.Extensions.Hosting
{
public static class Host

这个静态类 Host 提供了一个静态方法 CreateDefaultBuilder 来构建 IHostBuilder 实例。可以看到,第一行就是通过 new 操作符创建了 HostBuilder 对象实例。这个时候还没有依赖注入容器。

public static IHostBuilder CreateDefaultBuilder(string[] args)
{
var builder = new HostBuilder(); builder.UseContentRoot(Directory.GetCurrentDirectory());
builder.ConfigureHostConfiguration(config =>
{
config.AddEnvironmentVariables(prefix: "DOTNET_");
if (args != null)
{
config.AddCommandLine(args);
}
}); 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() && !string.IsNullOrEmpty(env.ApplicationName))
{
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) =>
{
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); // IMPORTANT: This needs to be added *before* configuration is loaded, this lets
// the defaults be overridden by the configuration.
if (isWindows)
{
// Default the EventLogLoggerProvider to warning or above
logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
} logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
logging.AddEventSourceLogger(); if (isWindows)
{
// Add the EventLogLoggerProvider on windows machines
logging.AddEventLog();
}
})
.UseDefaultServiceProvider((context, options) =>
{
var isDevelopment = context.HostingEnvironment.IsDevelopment();
options.ValidateScopes = isDevelopment;
options.ValidateOnBuild = isDevelopment;
}); return builder;
}

从源代码中我们可以看到,CreateDefaultBuilder方法配置了默认的ContentRoot配置项,用于指示应用程序从哪里获取静态文件和WEB程序的根目录。加载了DOTNET_前缀的系统环境变量,并从appsettings.jsonappsettings.{env.EnvironmentName}.json文件中加载应用程序配置信息。配置日志组件,并添加了日志系统的输出路径,而且针对Windows操作系统额外添加了日志的拦截器和WindowsEvent组件的日志输出路径。

扩展方法 ConfigureWebHostDefaults

Web 的处理是在哪里配置的呢?

ConfigureWebHostDefaults() 显然是 IHostBuilder 的一个扩展方法,在 GitHub 中查看 GenericHostBuilderExtensions 源码。它的代码并不复杂。

public static class GenericHostBuilderExtensions {
/// <summary>
/// Initializes a new instance of the <see cref="IWebHostBuilder"/> class with pre-configured defaults.
/// </summary>
/// <remarks>
/// The following defaults are applied to the <see cref="IWebHostBuilder"/>:
/// use Kestrel as the web server and configure it using the application's configuration providers,
/// adds the HostFiltering middleware,
/// adds the ForwardedHeaders middleware if ASPNETCORE_FORWARDEDHEADERS_ENABLED=true,
/// and enable IIS integration.
/// </remarks>
/// <param name="builder">The <see cref="IHostBuilder" /> instance to configure</param>
/// <param name="configure">The configure callback</param>
/// <returns>The <see cref="IHostBuilder"/> for chaining.</returns>
public static IHostBuilder ConfigureWebHostDefaults (this IHostBuilder builder, Action<IWebHostBuilder> configure) {
return builder.ConfigureWebHost (webHostBuilder => {
WebHost.ConfigureWebDefaults (webHostBuilder); configure (webHostBuilder);
});
}
}

这个 ConfigureWebHost 来自 IHostBuilder 的扩展方法,在 GitHub 中查看 GenericHostWebHostBuilderExtensions 源代码

public static class GenericHostWebHostBuilderExtensions {
public static IHostBuilder ConfigureWebHost (
this IHostBuilder builder,
Action<IWebHostBuilder> configure) {
var webhostBuilder = new GenericWebHostBuilder (builder);
configure (webhostBuilder);
builder.ConfigureServices (
(context, services) => services.AddHostedService<GenericWebHostService> ()
);
return builder;
}
}

可以看到,它内部又调用了 WebHost 中的 ConfigureWebDefaults 方法。

在 GitHub 中查看 WebHost 源代码

internal static void ConfigureWebDefaults (IWebHostBuilder builder) {
builder.ConfigureAppConfiguration ((Action<WebHostBuilderContext, IConfigurationBuilder>) ((ctx, cb) => {
if (!ctx.HostingEnvironment.IsDevelopment ())
return;
StaticWebAssetsLoader.UseStaticWebAssets (ctx.HostingEnvironment, ctx.Configuration);
})); builder.UseKestrel ((Action<WebHostBuilderContext, KestrelServerOptions>) ((builderContext, options) => options.Configure ((IConfiguration) builderContext.Configuration.GetSection ("Kestrel")))).ConfigureServices ((Action<WebHostBuilderContext, IServiceCollection>) ((hostingContext, services) => {
services.PostConfigure<HostFilteringOptions> ((Action<HostFilteringOptions>) (options => {
if (options.AllowedHosts != null && options.AllowedHosts.Count != 0)
return;
string str = hostingContext.Configuration["AllowedHosts"];
string[] strArray1;
if (str == null)
strArray1 = (string[]) null;
else
strArray1 = str.Split (new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
string[] strArray2 = strArray1;
HostFilteringOptions filteringOptions = options;
string[] strArray3;
if (strArray2 == null || strArray2.Length == 0)
strArray3 = new string[1] { "*" };
else
strArray3 = strArray2;
filteringOptions.AllowedHosts = (IList<string>) strArray3;
}));
services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>> ((IOptionsChangeTokenSource<HostFilteringOptions>) new ConfigurationChangeTokenSource<HostFilteringOptions> (hostingContext.Configuration));
services.AddTransient<IStartupFilter, HostFilteringStartupFilter> ();
if (string.Equals ("true", hostingContext.Configuration["ForwardedHeaders_Enabled"], StringComparison.OrdinalIgnoreCase)) {
services.Configure<ForwardedHeadersOptions> ((Action<ForwardedHeadersOptions>) (options => {
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.KnownNetworks.Clear ();
options.KnownProxies.Clear ();
}));
services.AddTransient<IStartupFilter, ForwardedHeadersStartupFilter> ();
}
services.AddRouting ();
})
.UseIIS ()
.UseIISIntegration ();
}

从源代码中我们可以看到,ConfigureWebDefaults() 方法配置了 Web Server 组件 Kestrel 用来处理HTTP通信,加载了 CORS 基础配置,配置了 ForwardedHeaders(用于支持代理服务如Nginx等),添加了基础的路由组件并配置了IIS的支持组件。 于是ASP.NET Core应用程序此时已经可以支持 HTTP 通信,并且无需额外配置就可以运行在代理服务器之上。还可以根据请求的URI信息进行路由分配,查找对应的 Controller类/Action方法。

UseStartup

扩展方法 - UseStartup

UseStartup 有一个较长的调用链,最终实际对ASP.NET Core应用程序进行配置的方法是 WebHostBuilderExtensions.UseStartup 方法 WebHostBuilderExtensions.UseStartup。 方法定义在WebHostBuilderExtensions 源代码

UseStartup 方法

public static IWebHostBuilder UseStartup(
this IWebHostBuilder hostBuilder,
Type startupType)
{
string name = startupType.GetTypeInfo().Assembly.GetName().Name;
hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, name);
return hostBuilder is ISupportsStartup supportsStartup ? supportsStartup.UseStartup(startupType) : hostBuilder.ConfigureServices((Action<IServiceCollection>) (services =>
{
if (typeof (IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
ServiceCollectionServiceExtensions.AddSingleton(services, typeof (IStartup), startupType);
else
ServiceCollectionServiceExtensions.AddSingleton(services, typeof (IStartup), (Func<IServiceProvider, object>) (sp =>
{
IHostEnvironment requiredService = sp.GetRequiredService<IHostEnvironment>();
return (object) new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, requiredService.EnvironmentName));
}));
}));
}

UseStartup 方法将会在应用程序中查找注册的 Startup 类,并执行 Startup 类的配置方法,对ASP.NET Core应用程序执行最后的,用户层面的最终配置,在 Startup 类中,我们将详细的配置我们的ASP.NET Core应用程序已满足我们对业务支撑的需要。这也是绝大多数开发者首次接触到的ASP.NET Core应用程序配置的地点。 值得注意的是,在 UseStartup 方法中,会首先查找声明了 IStartup 接口的 Startup 类(如果此处传入的 IWebHostBuiler 声明了 ISupportStartup 接口的话。)。因此除了将 ASP.NET Core 应用程序直接编译成可执行程序外,我们也可以将 ASP.NET Core 应用程序写在类库类型的项目中,由类库的使用者来帮助我们启动 ASP.NET Core应用程序。

终点 - Startup.cs

最后,我们再看一下默认生成的 Startup 类:

namespace SmapleWeb
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} app.UseRouting(); app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
}
}

空 ASP.NET Core项目的 Startup 类非常简单,实现了一个空的 ConfigureServices 方法,在这个方法里,我们可以注入我们需要使用的各种服务类或基础组件类。 并且实现了 Configure() 方法,注册了基础路由组件和终结点组件,能够处理访问 WEB ROOT 的路径的请求,并返回 Hello World!

以上就是 ASP.NET Core应用程序关于HOST类的全部源代码,我们可以看到 ASP.NET Core应用程序的宿主类的实现并不复杂,并且除了支持 ASP.NET Core应用程序之外,还可以支持我们方便的写出各种类型的服务程序,只需要我们根据实际需要实现一些基本类型即可。

ASP.NET Core IHostBuilder的更多相关文章

  1. asp.net core 系列 17 通用主机 IHostBuilder

    一.概述 ASP.NET Core 通用主机 (HostBuilder),该主机对于托管不处理 HTTP 请求的应用非常有用.通用主机的目标是将 HTTP 管道从 Web 主机 API 中分离出来,从 ...

  2. ASP.NET Core 3.0 上的gRPC服务模板初体验(多图)

    早就听说ASP.NET Core 3.0中引入了gRPC的服务模板,正好趁着家里电脑刚做了新系统,然后装了VS2019的功夫来体验一把.同时记录体验的过程.如果你也想按照本文的步骤体验的话,那你得先安 ...

  3. asp.net core 系列 16 Web主机 IWebHostBuilder

    一.概述 在asp.net core中,Host主机负责应用程序启动和生存期管理.host主机包括Web 主机(IWebHostBuilder)和通用主机(IHostBuilder).Web 主机是适 ...

  4. ASP.NET Core 3.0预览版体验

    目前.NET Core 3.0的版本为.NET Core 3.0 Preview 3,对应ASP.NET Core 3.0 Preview 3. ASP.NET Core 3.0 之后将不再支持.NE ...

  5. ASP.NET Core 2.2 迁移至 3.0 备忘录

    将 ASP.NET Core 2.2 迁移至 ASP.NET Core 3.0 需要注意的地方记录在这篇随笔中. TargetFramework 改为 netcoreapp3.0 <Target ...

  6. ASP.NET Core的Kestrel服务器(转载)

    Kestrel是一个基于libuv的跨平台ASP.NET Core web服务器,libuv是一个跨平台的异步I/O库.ASP.NET Core模板项目使用Kestrel作为默认的web服务器.Kes ...

  7. ASP.NET Core 如何设置发布环境

    在ASP.NET Core中自带了一些内置对象,可以读取到当前程序处于什么样的环境当中,比如在ASP.NET Core的Startup类的Configure方法中,我们就会看到这么一段代码: publ ...

  8. ASP.NET Core 中的管道机制

    首先,很感谢在上篇文章 C# 管道式编程 中给我有小额捐助和点赞的朋友们,感谢你们的支持与肯定.希望我的每一次分享都能让彼此获得一些收获,当然如果我有些地方叙述的不正确或不当,还请不客气的指出.好了, ...

  9. (15)ASP.NET Core Web主机(IWebHostBuilder)

    1.前言 ASP.NET Core应用程序可以配置和启动主机(Host).主机负责应用程序启动和生存期管理,配置服务器和请求处理管道.主机还可以设置日志记录.依赖关系注入和配置.而host主机又包括W ...

  10. (16)ASP.NET Core 通用主机(HostBuilder)

    1.前言 ASP.NET Core应用程序可以配置和启动主机(Host).主机负责应用程序启动和生命周期管理.通用主机用于无法处理HTTP请求的应用程序.通用主机的用途是将HTTP管道从Web主机AP ...

随机推荐

  1. Vue 文件流预览 PDF

    Vue js // pdf 预览 export function download(id) { return request({ url: '/bbs/regtech/law/download?id= ...

  2. Hive--hbase--spark

    hive创建hbase表 create external table events.hb_train( row_key string, user_id string, event_id string, ...

  3. react native 环境搭建遇到问题

    关于pod install 慢的要死的问题 解决方法记录     开始时显示ruby有问题,卸载cocoapods重装的   然后启动不起来,到ios目录下执行 pod install   然后太慢尝 ...

  4. USB协议详解第11讲(USB描述符-总结)

    描述符回顾总结 1.其实所有的描述符都是USB设备用来描述自己属性及用途的,所以必须在设备端实现对应的描述符,主机会在枚举此设备的时候根据设备实现的描述符去确定设备到底是一个什么样的设备.设备需要的总 ...

  5. day11-基本运算符

    运算符 java语言支持如下运算符: 优先级 ( 多敲,多练习 ) 算术运算符:+,-,*,/,%(模运算:取余),++,--  package operator; ​ public class De ...

  6. 云原生爱好者周刊:Crossplane 成为 CNCF 孵化项目

    云原生一周动态要闻: Crossplane 成为 CNCF 孵化项目 VMware Tanzu Kubernetes Grid 1.4 发布 Sqlcommenter 与 OpenTelemetry ...

  7. Java新特性-stream流

    Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据. Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达 ...

  8. python项目实战——人生重开模拟器

    文章目录 1.菜单栏的编写 2.玩家确定颜值.体质.智力.家境 3.生成性别 4.设定角色出生点 5.各个年龄段的变化 5.1 幼年阶段 5.2 青年阶段 5.3中年阶段 5.4 晚年阶段 6.整体代 ...

  9. manim边学边做--通用二维坐标系

    Manim的Axes对象是通用的坐标系对象,之前几篇介绍的数轴和各种坐标平面都是继承Axes对象. Axes对象的主要作用在于创建和管理二维坐标轴,以满足我们制作数学动画时的各种需求. 具体来说,Ax ...

  10. 题解:洛谷P1119 灾后重建

    题解:洛谷P1119 灾后重建 题目传送门 前言:没有掌握floyed求最短路的精髓是每次增加选一个中转点,导致写了2h才勉强卡过 法1:最暴力的想法就是开个三维数组把前i个点的dis状态全部存下来, ...