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.json和appsettings.{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. Java日期时间API系列40-----中文语句中的时间语义识别(time NLP)代码实现分析

    从上篇 Java日期时间API系列39-----中文语句中的时间语义识别(time NLP 输入一句话,能识别出话里的时间)原理分析 中得知解析的主要步骤分为三步: (1)加载正则文件 (2)解析中文 ...

  2. 墨天轮最受DBA欢迎的数据库技术文档-SQL优化篇

    [墨天轮最受欢迎的技术文档]系列文章得到了很多朋友的支持,大家也告诉了我们期待看到的主题这不!大家想看的优化系列-SQL优化篇来啦~原文文末送墨值中,欢迎大家参与! 数据库的调优被分成多个阶段,每个阶 ...

  3. iOS中NSBundle使用小结

    bundle是一个目录,其中包含了程序会使用到的资源. 这些资源包含了如图像,声音,文本文件,属性列表,语言包,编译好的代码,nib文件(用户也会把bundle称为plug-in). 对应bundle ...

  4. docker-compose -- 创建 redis && mysql

    version: '3' services: nest-admin-web: image: buqiyuan/vue3-antdv-admin:stable container_name: nest- ...

  5. query和params的区别

    2者都是编程式路由跳转用来存放传递数据的位置,query使用path引入,数据显示在地址栏上,params使用name引入,传递的数据在页面看不见 :

  6. 云原生周刊:KubeSphere 3.4.1 发布 | 2023.11.13

    开源项目推荐 Inspektor Gadget Inspektor Gadget 是一组用于调试和检查 Kubernetes 资源与应用程序的工具(或小工具).它在 Kubernetes 集群中管理 ...

  7. ​Meteor:全栈JavaScript开发框架的先驱

    Meteor 是一个功能强大且易用的全栈 JavaScript 框架,它简化了现代 Web 应用的开发流程,从数据库到用户界面都可以通过一套统一的技术栈来实现.无论你是构建实时应用还是想要一个快速的原 ...

  8. 【Kernel】基于 QEMU 的 Linux 内核编译和安装

    目录 安装虚拟机系统 共享目录 编译内核 卸载内核 参考资料 本文主要记录个人做存储系统研究时,在 QEMU 环境下编译和安装 Linux 内核的过程 安装虚拟机系统 之前在 利用 RocksDB + ...

  9. React 防抖(Debounce)Hook

    防抖动 (debounce)是常见的需求,那么如何搭配 React Hooks 实现防抖动呢,下面演示两种方案. 方案一 副作用防抖 /** * * @param {React.EffectCallb ...

  10. ConsulManager应用场景2:如何优雅的使用Consul管理Blackbox站点监控

    [ConsulManager介绍] Consul字段设计说明 所有数据存在一个名为blackbox_exporter的Services项中,每个监控目标为一个子Service. 每个Service使用 ...