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 ...
随机推荐
- 立足信创国产化运维,打造安全可控IT运维管理系统
随着国产化信创应用试点行业的不断扩大,应用信创产品的企事业单位逐渐增多.大多数企业均面临着陌生的国产化环境与产品,其使用习惯和解决问题的方式都面临改变.北京智和信通切实立足用户需求,提供信创运维服务. ...
- 10款好用的开源 HarmonyOS 工具库
大家好,我是 V 哥,今天给大家分享10款好用的 HarmonyOS的工具库,在开发鸿蒙应用时可以用下,好用的工具可以简化代码,让你写出优雅的应用来.废话不多说,马上开整. 1. efTool efT ...
- balance_dirty_pages_ratelimited分析
balance_dirty_pages_ratelimited分析 nr_dirtied_pause:当前task的脏页门限: dirty_exceeded:全局的脏页数超过门限或者该bdi的脏页数超 ...
- Android Qcom USB Driver学习(十一)
基于TI的Firmware Update固件升级的流程分析usb appliction layers的数据 USB Protocol Package ①/② map to check password ...
- usb请求块以及提交方式
URB结构体 struct urb { /* private: usb core and host controller only fields in the urb */ struct kref k ...
- 2024年4月中国数据库排行榜:OceanBase再度登顶,KingBase稳步上升进前五
春风劲吹,迎来了2024年4月中国流行度排行榜.纵观本月榜单,各家数据库产品你追我赶,名次呈现微妙变动,它们正以不可忽视的力量,推动着中国乃至全球的数据管理革新.在这春意盎然的四月,让我们继续关注这些 ...
- DIY Matter Bridge 和智能锁简单联动的实践
一. 写在前面 在之前的博客文章 <基于乐鑫 ESP32-C3 的 Matter Light 实践>中,我们利用乐鑫的硬件和 SDK 方案实现了简单的 Light 例程,并对 Matter ...
- 优雅简单玩转python3异步并发
在python3之后,随着async/await引入,异步调用以全新而便捷的方式让人眼前一亮. 首先,尽量用async/await定义协程 这里以使用aiohttp请求网络,async函数中,不要使用 ...
- 高性能 Nginx HTTPS 调优 - 如何为 HTTPS 提速 30%
为什么要优化 Ngin HTTPS 延迟 Nginx 常作为最常见的服务器,常被用作负载均衡 (Load Balancer).反向代理 (Reverse Proxy),以及网关 (Gateway) 等 ...
- jQuery 杂项方法-grep()_20220114
jQuery 杂项方法-grep() 实例: var targetEmpArr = $.grep(empArr, function(elem,index){ return elem.empCode = ...