ASP.NET Core IHostBuilder
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方法的内容也很简单,只有一个基本检查和六个步骤。
- 首先检查是否重复创建了IHost对象,每个IHostBuilder对象只能运行一次Build方法,否则抛出异常,确保我们不会重复创建IHost对象。
- 第二步初始化创建IHost对象过程中需要使用到的基本配置信息。
- 第三步初始化应用程序的
Microsoft.Extensions.Hosting.IHostEnvironment对象,用来向应用程序提供程序运行环境信息(Development、Stage、Production等)。 - 第四步初始化(根据第二部初始化的基本配置信息)创建IHost对象所需要的所有上下文对象。
- 创建IOC容器,并将默认的IHost对象(
Microsoft.Extensions.Hosting.Internal.Host)注入到IOC容易中。 - 然后将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 是这个静态类。
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 方法。
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的更多相关文章
- asp.net core 系列 17 通用主机 IHostBuilder
一.概述 ASP.NET Core 通用主机 (HostBuilder),该主机对于托管不处理 HTTP 请求的应用非常有用.通用主机的目标是将 HTTP 管道从 Web 主机 API 中分离出来,从 ...
- ASP.NET Core 3.0 上的gRPC服务模板初体验(多图)
早就听说ASP.NET Core 3.0中引入了gRPC的服务模板,正好趁着家里电脑刚做了新系统,然后装了VS2019的功夫来体验一把.同时记录体验的过程.如果你也想按照本文的步骤体验的话,那你得先安 ...
- asp.net core 系列 16 Web主机 IWebHostBuilder
一.概述 在asp.net core中,Host主机负责应用程序启动和生存期管理.host主机包括Web 主机(IWebHostBuilder)和通用主机(IHostBuilder).Web 主机是适 ...
- 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 ...
- ASP.NET Core 2.2 迁移至 3.0 备忘录
将 ASP.NET Core 2.2 迁移至 ASP.NET Core 3.0 需要注意的地方记录在这篇随笔中. TargetFramework 改为 netcoreapp3.0 <Target ...
- ASP.NET Core的Kestrel服务器(转载)
Kestrel是一个基于libuv的跨平台ASP.NET Core web服务器,libuv是一个跨平台的异步I/O库.ASP.NET Core模板项目使用Kestrel作为默认的web服务器.Kes ...
- ASP.NET Core 如何设置发布环境
在ASP.NET Core中自带了一些内置对象,可以读取到当前程序处于什么样的环境当中,比如在ASP.NET Core的Startup类的Configure方法中,我们就会看到这么一段代码: publ ...
- ASP.NET Core 中的管道机制
首先,很感谢在上篇文章 C# 管道式编程 中给我有小额捐助和点赞的朋友们,感谢你们的支持与肯定.希望我的每一次分享都能让彼此获得一些收获,当然如果我有些地方叙述的不正确或不当,还请不客气的指出.好了, ...
- (15)ASP.NET Core Web主机(IWebHostBuilder)
1.前言 ASP.NET Core应用程序可以配置和启动主机(Host).主机负责应用程序启动和生存期管理,配置服务器和请求处理管道.主机还可以设置日志记录.依赖关系注入和配置.而host主机又包括W ...
- (16)ASP.NET Core 通用主机(HostBuilder)
1.前言 ASP.NET Core应用程序可以配置和启动主机(Host).主机负责应用程序启动和生命周期管理.通用主机用于无法处理HTTP请求的应用程序.通用主机的用途是将HTTP管道从Web主机AP ...
随机推荐
- el-table 宽度自适应bug
和 flex 一起使用的时候会有这个问题.只能自动变宽不能自动变窄. 在 flex-grow:1; 的那一层设置 overflow: auto;
- torch和numpy的相互转换
import torch x = torch.rand(2,2) x1 = x.numpy() # torch转换到numpy x2 = torch.from_numpy(x1) #numpy转换to ...
- 关于 apt 的一些命令
apt update 用于 更新 包索引,包列表 apt install -y open-vm-tools-desktop fuse 安装 vm-tools 工具
- package.json文件干什么的 ?
package.json 是项目描述文件,记录了当前项目的信息,比如项目的名字,版本,作者,还有所依赖的第三方模块 : dependencies 是项目依赖,是项目上线时要依赖的第三方包 : devd ...
- kotlin函数和Lambda表达式——>函数
函数: 1.函数声明 kotlin中的函数使用fun关键字声明: fun double(x: Int): Int { return 2 * x } 2.函数用法 调用函数使用传统的方法: val re ...
- 云原生周刊:Istio 1.19 发布 | 2023.9.11
开源项目推荐 Timoni Timoni 是 Kubernetes 的软件包管理器,由 CUE 提供支持,灵感来自 Helm. Timoni 项目致力于改善编写 Kubernetes 配置的用户体验. ...
- ToDesk再度出手,加快云电脑高性能发展,剑指千亿级市场
根据中国信通院发布的<云计算白皮书(2023年)>(以下简称白皮书)显示,云计算引发了软件开发部署模式的创新,成为承载各类应用的关键基础设施,为大数据.物联网.人工智能等新兴领域的发展提供 ...
- Java学习十七—反射机制:解锁代码的无限可能
Java学习十七-反射机制:解锁代码的无限可能 一.关于反射 1.1 简介 Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性.方法 ...
- 2.10 破解密码这么简单,Linux是安全的操作系统吗?
很多人看到系统修复模式这节内容时,都会有所感慨:Linux 的密码破解太容易了,这样的操作系统还安全吗?Linux 不是以安全性著称的吗? 图 1 密码安全 本节,我们结合图 1 解释一下类似的问题. ...
- Flink 中的事件时间触发器和处理时间触发器
EventTimeTrigger EventTimeTrigger 的触发完全依赖 watermark,换言之,如果 stream 中没有 watermark,就不会触发 EventTimeTrigg ...