Minimal API仅仅是在基于IHost/IHostBuilder的服务承载系统上作了小小的封装而已,它利用WebApplication和WebApplicationBuilder这两个类型提供了更加简洁的API,同时提供了与现有API的兼容。要成分理解Minimal API的实现原理,得先对服务承载系统有基本的理解,对此不了解的可以参阅《服务承载模型[上篇]》、《服务承载模型[下篇]》、《承载服务启动流程[上篇]》和《承载服务启动流程[下篇]》。对于本篇提供的模拟代码,可以从这里下载。

一、基础模型

二、WebApplication

三、WebApplication的构建
     1. BootstrapHostBuilder
     2. ConfigureHostBuilder
     3. ConfigureWebHostBuilder
     4. WebApplicationBuilder

四、 工厂方法

一、基础模型

对于由WebApplication和WebApplicationBuilder构建的承载模型,我们没有必要了解其实现的每一个细节,知道其大致的设计和实现原理就可以了,所以本节会采用最简洁的代码模拟这两个类型的实现。如图1所示,代表承载应用的WebApplication对象是对一个IHost对象的封装,而且该类型自身也实现了IHost接口, WebApplication对象其实还是作为一个IHost对象被启动的。作为构建这的WebApplicationBuilder则是对一个IHostBuilder对象的封装,它对WebApplication对象的构建体现在利用封装的IHostBuilder对象构建一个对应的IHost对象,最终利用后者将WebApplication对象创建出来。

图17-8 完整的请求处理管道

二、WebApplication

WebApplication类型不仅仅实现了IHost接口,还同时实现IApplicationBuilder接口,所以中间件可以直接注册到这个对象上的。该类型还实现了IEndpointRouteBuilder接口,所以我们还能利用它进行路由注册,我们在20章才会涉及到路由,所以我们现在先忽略针对该接口的实现。下面的代码模拟WebApplication类型的实现。如代码片段所示,WebApplication的构造函数定义了一个IHost类型的参数,它利用这个对象完成了对IHost接口所有成员的实现,针对IApplicationBuilder接口成员的实现则利用创建的ApplicationBuilder对象来完成。WebApplication还提供了一个BuildRequestDelegate方法利用这个ApplicationBuilder对象完成了对中间件管道的构建。

  1. public class WebApplication : IApplicationBuilder, IHost
  2. {
  3. private readonly IHost _host;
  4. private readonly ApplicationBuilder _app;
  5.  
  6. public WebApplication(IHost host)
  7. {
  8. _host = host;
  9. _app = new ApplicationBuilder(host.Services);
  10. }
  11.  
  12. IServiceProvider IHost.Services => _host.Services;
  13. Task IHost.StartAsync(CancellationToken cancellationToken) => _host.StartAsync(cancellationToken);
  14. Task IHost.StopAsync(CancellationToken cancellationToken) => _host.StopAsync(cancellationToken);
  15.  
  16. IServiceProvider IApplicationBuilder.ApplicationServices { get => _app.ApplicationServices; set => _app.ApplicationServices = value; }
  17. IFeatureCollection IApplicationBuilder.ServerFeatures => _app.ServerFeatures;
  18. IDictionary<string, object?> IApplicationBuilder.Properties => _app.Properties;
  19. RequestDelegate IApplicationBuilder.Build() => _app.Build();
  20. IApplicationBuilder IApplicationBuilder.New() => _app.New();
  21. IApplicationBuilder IApplicationBuilder.Use(Func<RequestDelegate, RequestDelegate> middleware) => _app.Use(middleware);
  22.  
  23. void IDisposable.Dispose() => _host.Dispose();
  24. public IServiceProvider Services => _host.Services;
  25. internal RequestDelegate BuildRequestDelegate() => _app.Build();
  26. ...
  27. }

WebApplication额外定义了如下的RunAsync和Run方法,它们分别以异步和同步方式启动承载的应用。调用这两个方法的时候可以指定监听地址,指定的地址被添加到IServerAddressesFeature特性中,而服务器正式利用这个特性来提供监听地址的。

  1. public class WebApplication : IApplicationBuilder, IHost
  2. {
  3. private readonly IHost _host;
  4.  
  5. public ICollection<string> Urls => _host.Services.GetRequiredService<IServer>().Features.Get<IServerAddressesFeature>()?.Addresses ?? throw new InvalidOperationException("IServerAddressesFeature is not found.");
  6.  
  7. public Task RunAsync(string? url = null)
  8. {
  9. Listen(url);
  10. return HostingAbstractionsHostExtensions.RunAsync(this);
  11. }
  12.  
  13. public void Run(string? url = null)
  14. {
  15. Listen(url);
  16. HostingAbstractionsHostExtensions.Run(this);
  17. }
  18.  
  19. private void Listen(string? url)
  20. {
  21. if (url is not null)
  22. {
  23. var addresses = _host.Services.GetRequiredService<IServer>().Features.Get<IServerAddressesFeature>()?.Addresses ?? throw new InvalidOperationException("IServerAddressesFeature is not found.");
  24. addresses.Clear();
  25. addresses.Add(url);
  26. }
  27. }
  28. ...
  29. }

三、WebApplication的构建

要创建一个WebApplication对象,只需要提供一个对应的IHost对象即可。IHost对象是通过IHostBuilder对象构建的,所以WebApplicationBuilder需要一个IHostBuilder对象,具体来说是一个HostBuilder对象。我们针对WebApplicationBuilder对象所作的一切设置最终都需要转移到这个HostBuilder对象上才能生效。为了提供更加简洁的API,WebApplicationBuilder类型提供了一系列的属性。比如它利用Serrvices属性提供了可以直接进行服务注册的IServiceCollection集合,利用Environment属性提供了表示当前承载环境的IWebHostEnvironment对象,利用Configuration属性提供的ConfigurationManager对象不仅可以作为IConfigurationBuilder对象帮助我们完成对配置系统的一切设置,它自身也可以作为IConfiguration对象为我们提供配置。

WebApplicationBuilder还定义了Host和WebHost属性,对应类型为ConfigureHostBuilder和ConfigureWebHostBuilder,它们分别实现了IHostBuilder和IWebHostBuilder接口,其目的是为了复用IHostBuilder和IWebHostBuilder接口承载的API(主要是扩展方法)。为了会尽可能使用现有方法对IHostBuilder对象进行初始化设置,它还使用了一个实现了IHostBuilder接口的BootstrapHostBuilder类型。有这些对象组成了WebApplicationBuilder针对HostBuilder的构建模型。如图2所示,WebApplicationBuilder的所有工作都是为了构建它封装的HostBuilder对象。

当WebApplicationBuilder初始化的时候,它除了会创建这个HostBuilder对象,还会创建存储服务注册的IServiceCollection对象,以及用来对配置进行设置的ConfigurationManager对象。接下来它会创建一个BootstrapHostBuilder对象,并将它参数调用相应的方法(比如ConfigureWebHostDefaults方法)将初始化设置收集起来,收集的服务注册和针对配置系统的设置分别转移到创建的IServiceCollection和ConfigurationManager对象中,其他设置直接应用到封装的HostBuilder对象上。

图2 HostBuilder构建模型

WebApplicationBuilder在此之后会创建出代表承载环境的IWebHostEnvironment对象,并对Environment属性进行初始化。在得到表示承载上下文的WebHostBuilderContext对象之后,上述的ConfigureHostBuilder和ConfigureWebHostBuilder对象被创建出来,并赋值给Host和WebHost属性。与BootstrapHostBuilder作用类似,我们利用这两个对象所作的设置最终都会转移到上述的三个对象中。当WebApplicationBuilder进行WebApplication对象构建的时候,IServiceCollection对象存储的服务注册和ConfigurationManager对象承载配置最终转移到HostBuilder对象上。此时再利用后者构建出对应的IHost对象,代表承载应用的WebApplication对象最终由该对象构建出来。

1. BootstrapHostBuilder

如下所示的是我们模拟的BootstrapHostBuilder类型的定义。正如上面所说,这个它的作用是收集初始化IHostBuilder对象提供的设置并将它们分别应用到指定的IServiceCollection、ConfigurationManager和IHostBuilder对象上。这一使命体现在BootstrapHostBuilder的Apply方法上,该方法还通过一个输出参数返回创建的HostBuilderContext上下文。

  1. public class BootstrapHostBuilder : IHostBuilder
  2. {
  3. private readonly List<Action<IConfigurationBuilder>> _configureHostConfigurations = new();
  4. private readonly List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigurations = new();
  5. private readonly List<Action<HostBuilderContext, IServiceCollection>> _configureServices = new();
  6. private readonly List<Action<IHostBuilder>> _others = new();
  7.  
  8. public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();
  9. public IHost Build() => throw new NotImplementedException();
  10. public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
  11. {
  12. _configureHostConfigurations.Add(configureDelegate);
  13. return this;
  14. }
  15. public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
  16. {
  17. _configureAppConfigurations.Add(configureDelegate);
  18. return this;
  19. }
  20. public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
  21. {
  22. _configureServices.Add(configureDelegate);
  23. return this;
  24. }
  25. public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
  26. {
  27. _others.Add(builder => builder.UseServiceProviderFactory(factory));
  28. return this;
  29. }
  30. public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory)
  31. {
  32. _others.Add(builder => builder.UseServiceProviderFactory(factory));
  33. return this;
  34. }
  35. public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)
  36. {
  37. _others.Add(builder => builder.ConfigureContainer(configureDelegate));
  38. return this;
  39. }
  40.  
  41. internal void Apply(IHostBuilder hostBuilder, ConfigurationManager configuration, IServiceCollection services, out HostBuilderContext builderContext)
  42. {
  43. // 初始化针对宿主的配置
  44. var hostConfiguration = new ConfigurationManager();
  45. _configureHostConfigurations.ForEach(it => it(hostConfiguration));
  46.  
  47. // 创建承载环境
  48. var environment = new HostingEnvironment()
  49. {
  50. ApplicationName = hostConfiguration[HostDefaults.ApplicationKey],
  51. EnvironmentName = hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production,
  52. ContentRootPath = HostingPathResolver.ResolvePath(hostConfiguration[HostDefaults.ContentRootKey])
  53. };
  54. environment.ContentRootFileProvider = new PhysicalFileProvider(environment.ContentRootPath);
  55.  
  56. // 创建HostBuilderContext上下文
  57. var hostContext = new HostBuilderContext(Properties)
  58. {
  59. Configuration = hostConfiguration,
  60. HostingEnvironment = environment,
  61. };
  62.  
  63. // 将针对宿主的配置添加到ConfigurationManager中
  64. configuration.AddConfiguration(hostConfiguration, true);
  65.  
  66. // 初始化针对应用的配置
  67. _configureAppConfigurations.ForEach(it => it(hostContext, configuration));
  68.  
  69. // 收集服务注册
  70. _configureServices.ForEach(it => it(hostContext, services));
  71.  
  72. // 将针对依赖注入容器的设置应用到指定的IHostBuilder对象上
  73. _others.ForEach(it => it(hostBuilder));
  74.  
  75. // 将自定义属性转移到指定的IHostBuilder对象上
  76. foreach (var kv in Properties)
  77. {
  78. hostBuilder.Properties[kv.Key] = kv.Value;
  79. }
  80.  
  81. builderContext = hostContext;
  82. }
  83. }

除了Build方法,IHostBuilder接口中定义的所有方法的参数都是委托,所以实现的这些方法将提供的委托收集起来。在Apply方法中,我们通过执行这些委托对象,将初始化设置应用到指定的IServiceCollection、ConfigurationManager和IHostBuilder对象上,并根据初始化宿主配置构建出代表承载环境的HostingEnvironment对象。该方法最后根据承载环境结合配置将HostBuilderContext上下文创建出来,并以输出参数的形式返回。

  1. internal static class HostingPathResolver
  2. {
  3. public static string ResolvePath(string? contentRootPath) => ResolvePath(contentRootPath, .BaseDirectory);
  4. public static string ResolvePath(string? contentRootPath, string basePath) => string.IsNullOrEmpty(contentRootPath)
  5. ? Path.GetFullPath(basePath): Path.IsPathRooted(contentRootPath)? Path.GetFullPath(contentRootPath)
  6. : Path.GetFullPath(Path.Combine(Path.GetFullPath(basePath), contentRootPath));
  7. }

2. ConfigureHostBuilder

ConfigureHostBuilder是在应用了BootstrapHostBuilder收集的初始化设置之后创建的,在创建该对象时提供了HostBuilderContext上下文, ConfigurationManager和IServiceCollection对象。提供的服务注册直接添加到IServiceCollection对象中,针对配置的设置已经应用到ConfigurationManager对象,直接针对IHostBuilder对象的设置则利用_configureActions字段暂存起来。

  1. public class ConfigureHostBuilder : IHostBuilder
  2. {
  3. private readonly ConfigurationManager _configuration;
  4. private readonly IServiceCollection _services;
  5. private readonly HostBuilderContext _context;
  6. private readonly List<Action<IHostBuilder>> _configureActions = new();
  7.  
  8. internal ConfigureHostBuilder(HostBuilderContext context, ConfigurationManager configuration, IServiceCollection services)
  9. {
  10. _configuration = configuration;
  11. _services = services;
  12. _context = context;
  13. }
  14.  
  15. public IDictionary<object, object> Properties => _context.Properties;
  16. public IHost Build() => throw new NotImplementedException();
  17. public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
  18. => Configure(() => configureDelegate(_context, _configuration));
  19.  
  20. public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
  21. {
  22. var applicationName = _configuration[HostDefaults.ApplicationKey];
  23. var contentRoot = _context.HostingEnvironment.ContentRootPath;
  24. var environment = _configuration[HostDefaults.EnvironmentKey];
  25.  
  26. configureDelegate(_configuration);
  27.  
  28. // 与环境相关的三个配置不允许改变
  29. Validate(applicationName, HostDefaults.ApplicationKey, "Application name cannot be changed.");
  30. Validate(contentRoot, HostDefaults.ContentRootKey, "Content root cannot be changed.");
  31. Validate(environment, HostDefaults.EnvironmentKey, "Environment name cannot be changed.");
  32.  
  33. return this;
  34.  
  35. void Validate(string previousValue, string key, string message)
  36. {
  37. if (!string.Equals(previousValue, _configuration[key], StringComparison.OrdinalIgnoreCase))
  38. {
  39. throw new NotSupportedException(message);
  40. }
  41. }
  42. }
  43.  
  44. public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
  45. => Configure(() => configureDelegate(_context, _services));
  46.  
  47. public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
  48. => Configure(() => _configureActions.Add(b => b.UseServiceProviderFactory(factory)));
  49.  
  50. public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory)
  51. => Configure(() => _configureActions.Add(b => b.UseServiceProviderFactory(factory)));
  52.  
  53. public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)
  54. => Configure(() => _configureActions.Add(b => b.ConfigureContainer(configureDelegate)));
  55.  
  56. private IHostBuilder Configure(Action configure)
  57. {
  58. configure();
  59. return this;
  60. }
  61.  
  62. internal void Apply(IHostBuilder hostBuilder) => _configureActions.ForEach(op => op(hostBuilder));
  63. }

WebApplicationBuilder对象一旦被创建出来后,针对承载环境的配置是不能改变的,所以ConfigureHostBuilder的ConfigureHostConfiguration方法针对此添加了相应的验证。两个UseServiceProviderFactory方法和ConfigureContainer方法针对依赖注入容器的设置最终需要应用到IHostBuilder对象上,所以我们将方法中提供的委托对象利用configureActions字段存起来,并最终利用Apply方法应用到指定的IHostBuilder对象上。

3. ConfigureWebHostBuilder

ConfigureWebHostBuilder同样是在应用了BootstrapHostBuilder提供的初始化设置后创建的,创建该对象时能够提供WebHostBuilderContext上下文和承载配置和服务注册的ConfigurationManager和IServiceCollection对象。由于IWebHostBuilder接口定义的方法只涉及服务注册和针对配置的设置,所以方法提供的委托对象可以直接应用到这两个对象上。

  1. public class ConfigureWebHostBuilder : IWebHostBuilder, ISupportsStartup
  2. {
  3. private readonly WebHostBuilderContext _builderContext;
  4. private readonly IServiceCollection _services;
  5. private readonly ConfigurationManager _configuration;
  6.  
  7. public ConfigureWebHostBuilder(WebHostBuilderContext builderContext, ConfigurationManager configuration, IServiceCollection services)
  8. {
  9. _builderContext = builderContext;
  10. _services = services;
  11. _configuration = configuration;
  12. }
  13.  
  14. public IWebHost Build() => throw new NotImplementedException();
  15. public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate) => Configure(() => configureDelegate(_builderContext, _configuration));
  16. public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices) => Configure(() => configureServices(_services));
  17. public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices) => Configure(() => configureServices(_builderContext, _services));
  18. public string? GetSetting(string key) => _configuration[key];
  19. public IWebHostBuilder UseSetting(string key, string? value) => Configure(() => _configuration[key] = value);
  20.  
  21. IWebHostBuilder ISupportsStartup.UseStartup(Type startupType) => throw new NotImplementedException();
  22. IWebHostBuilder ISupportsStartup.UseStartup<TStartup>(Func<WebHostBuilderContext, TStartup> startupFactory) => throw new NotImplementedException();
  23. IWebHostBuilder ISupportsStartup.Configure(Action<IApplicationBuilder> configure) => throw new NotImplementedException();
  24. IWebHostBuilder ISupportsStartup.Configure(Action<WebHostBuilderContext, IApplicationBuilder> configure) => throw new NotImplementedException();
  25.  
  26. private IWebHostBuilder Configure(Action configure)
  27. {
  28. configure();
  29. return this;
  30. }
  31. }

我们在前面说过,传统承载方式将初始化操作定义在注册的Startup类型的编程方式在Minima API中已经不再被支持了,所以WebApplicationBuilder本不该实现ISupportsStartup接口,但是我们希望用户在采用这种编程方式时得到显式的提醒,所以依然让它实现该接口,并在实现的方法中抛出NotImplementedException类型的异常。

4. WebApplicationBuilder

如下的代码片段模拟了WebApplicationBuilder针对WebApplication的构建。它的构造函数会创建一个BootstrapHostBuilder对象,调用它的ConfigureDefaults和ConfigureWebHostDefaults扩展方法将初始化设置收集起来。ConfigureWebHostDefaults方法会利用提供的Action<IWebHostBuilder>委托进行中间件的注册,由于中间件的注册被转移到WebApplication对象上,并且它提供了一个BuildRequestDelegate方法返回由注册中间件组成的管道,所以在这里只需调用构建的WebApplication对象(通过_application字段表示,虽然此时尚未创建,但是中间件真正被注册时会被创建出来)的这个方法,并将返回的RequestDelegate对象作为参数调用IApplicationBuilder接口的Run方法将中间件管道注册为请求处理器。

  1. public class WebApplicationBuilder
  2. {
  3. private readonly HostBuilder _hostBuilder = new HostBuilder();
  4. private WebApplication _application;
  5.  
  6. public ConfigurationManager Configuration { get; } = new ConfigurationManager();
  7. public IServiceCollection Services { get; } = new ServiceCollection();
  8. public IWebHostEnvironment Environment { get; }
  9. public ConfigureHostBuilder Host { get; }
  10. public ConfigureWebHostBuilder WebHost { get; }
  11. public ILoggingBuilder Logging { get; }
  12.  
  13. public WebApplicationBuilder(WebApplicationOptions options)
  14. {
  15. //创建BootstrapHostBuilder并利用它收集初始化过程中设置的配置、服务和针对依赖注入容器的设置
  16. var args = options.Args;
  17. var bootstrap = new BootstrapHostBuilder();
  18. bootstrap
  19. .ConfigureDefaults(null)
  20. .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder.Configure(app => app.Run(_application.BuildRequestDelegate())))
  21. .ConfigureHostConfiguration(config => {
  22. // 添加命令行配置源
  23. if (args?.Any() == true)
  24. {
  25. config.AddCommandLine(args);
  26. }
  27.  
  28. // 将WebApplicationOptions配置选项转移到配置中
  29. Dictionary<string, string>? settings = null;
  30. if (options.EnvironmentName is not null) (settings ??= new())[HostDefaults.EnvironmentKey] = options.EnvironmentName;
  31. if (options.ApplicationName is not null) (settings ??= new())[HostDefaults.ApplicationKey] = options.ApplicationName;
  32. if (options.ContentRootPath is not null) (settings ??= new())[HostDefaults.ContentRootKey] = options.ContentRootPath;
  33. if (options.WebRootPath is not null) (settings ??= new())[WebHostDefaults.WebRootKey] = options.EnvironmentName;
  34. if (settings != null)
  35. {
  36. config.AddInMemoryCollection(settings);
  37. }
  38. });
  39.  
  40. // 将BootstrapHostBuilder收集到配置和服务转移到Configuration和Services上
  41. // 将应用到BootstrapHostBuilder上针对依赖注入溶质的设置转移到_hostBuilder上
  42. // 得到BuilderContext上下文
  43. bootstrap.Apply(_hostBuilder, Configuration, Services, out var builderContext);
  44.  
  45. // 如果提供了命令行参数,在Configuration上添加对应配置源
  46. if (options.Args?.Any() == true)
  47. {
  48. Configuration.AddCommandLine(options.Args);
  49. }
  50.  
  51. // 构建WebHostBuilderContext上下文
  52. // 初始化Host、WebHost和Logging属性
  53. var webHostContext = (WebHostBuilderContext)builderContext.Properties[typeof(WebHostBuilderContext)];
  54. Environment = webHostContext.HostingEnvironment;
  55. Host = new ConfigureHostBuilder(builderContext, Configuration, Services);
  56. WebHost = new ConfigureWebHostBuilder(webHostContext, Configuration, Services);
  57. Logging = new LogginigBuilder(Services);
  58. }
  59.  
  60. public WebApplication Build()
  61. {
  62. // 将ConfigurationManager的配置转移到_hostBuilder
  63. _hostBuilder.ConfigureAppConfiguration(builder =>
  64. {
  65. builder.AddConfiguration(Configuration);
  66. foreach (var kv in ((IConfigurationBuilder)Configuration).Properties)
  67. {
  68. builder.Properties[kv.Key] = kv.Value;
  69. }
  70. });
  71.  
  72. // 将添加的服务注册转移到_hostBuilder
  73. _hostBuilder.ConfigureServices((_, services) =>
  74. {
  75. foreach (var service in Services)
  76. {
  77. services.Add(service);
  78. }
  79. });
  80.  
  81. // 将应用到Host属性上的设置转移到_hostBuilder
  82. Host.Apply(_hostBuilder);
  83.  
  84. // 利用_hostBuilder构建的IHost对象创建WebApplication
  85. return _application = new WebApplication(_hostBuilder.Build());
  86. }
  87. }

接下来BootstrapHostBuilder的ConfigureHostConfiguration方法被调用,我们利用它将提供的WebApplicationOptions配置选项转移到BootstrapHostBuilder针对宿主的配置上。针对IHostBuilder初始化设置应用到BootstrapHostBuilder对象上之后,我们调用其Apply方法将这些设置分别转移到承载服务注册和配置的IServiceCollection和ConfigurationManager对象,以及封装的HostBuilder对象上。Apply方法利用输出参数提供了HostBuilderContext上下文,我们进一步从中提取出WebHostBuilderContext上下文(GenericWebHostBuilder会将构建的WebHostBuilderContext上下文置于HostBuilderContext对象的属性字典中)。我们利用这个上下文将ConfigureHostBuilder和ConfigureWebHostBuilder对象创建出来,并作为Host和WebHost属性。用于对日志做进一步设置的Logging属性也在这里被初始化,返回的LoggingBuilder对象仅仅是对IServiceCollection对象的简单封装而已。

构建WebApplication对象的Build方法分别调用ConfigureAppConfiguration和ConfigureServices方法将ConfigurationManager和IServiceCollection对象承载的配置和服务注册转移到HostBuilder对象上。它接下来提取出Host属性返回的ConfigureHostBuilder对象,并调用其Apply方法将应用在该对象上针对依赖注入容器的设置转移到HostBuilder对象上。至此所有的设置全部转移到了HostBuilder对象上,我们调用其Build方法构建出对应的IHost对象后,最后利用后者将代码承载应用的WebApplication对象构建出来。我们将这个对象赋值到_application字段上,前面调用ConfigureWebHostDefaults扩展方法提供的委托会将它的BuildRequestDelegate方法构建的中间件管道作为请求处理器。

四、 工厂方法

代表承载应用的WebApplication对象由WebApplicationBuilder构建的,但是我们一般不会通过调用构造函数的方式来创建WebApplicationBuilder对象,这有违“面向接口”编程的原则,所以我们都会使用WebApplication类型提供的静态工厂方法来创建它。WebApplication除了提供了三个用于创建WebApplicationBuilder的CreateBuilder方法重载,还提供了一个直接创建WebApplication对象的Create方法。

  1. public sealed class WebApplication
  2. {
  3. public static WebApplicationBuilder CreateBuilder() => new WebApplicationBuilder(new WebApplicationOptions());
  4.  
  5. public static WebApplicationBuilder CreateBuilder(string[] args)
  6. {
  7. var options = new WebApplicationOptions();
  8. options.Args = args;
  9. return new WebApplicationBuilder(options);
  10. }
  11.  
  12. public static WebApplicationBuilder CreateBuilder(WebApplicationOptions options) => new WebApplicationBuilder(options, null);
  13.  
  14. public static WebApplication Create(string[]? args = null)
  15. {
  16. var options = new WebApplicationOptions();
  17. options.Args = args;
  18. return new WebApplicationBuilder(options).Build();
  19. }
  20. }

本节内容通过针对WebApplication和WebApplicationBuilder这两个类型的实现模拟来讲解Minimal API的实现原理。一方面为了让讲解更加清晰,另一方面也出于篇幅的限制,不得不省去很多细枝末节的内容,但是设计思想和实现原理别无二致。上面提供的源代码也不是伪代码,如下所示的就是在“模拟的Minimal API”构建的ASP.NET Core应用,它是可以正常运行的。如果读者朋友们对真实的实现感兴趣,可以将它作为一个“向导”去探寻“真实的Minimal API”。

  1. var app = App.WebApplication.Create();
  2. app.Run(httpContext => httpContext.Response.WriteAsync("Hello World!"));
  3. app.Run();

ASP.NET Core 6 Minimal API的模拟实现的更多相关文章

  1. angular4和asp.net core 2 web api

    angular4和asp.net core 2 web api 这是一篇学习笔记. angular 5 正式版都快出了, 不过主要是性能升级. 我认为angular 4还是很适合企业的, 就像.net ...

  2. ASP.NET Core 中基于 API Key 对私有 Web API 进行保护

    这两天遇到一个应用场景,需要对内网调用的部分 web api 进行安全保护,只允许请求头账户包含指定 key 的客户端进行调用.在网上找到一篇英文博文 ASP.NET Core - Protect y ...

  3. ASP.NET Core WebApi构建API接口服务实战演练

    一.ASP.NET Core WebApi课程介绍 人生苦短,我用.NET Core!提到Api接口,一般会想到以前用到的WebService和WCF服务,这三个技术都是用来创建服务接口,只不过Web ...

  4. 使用ASP.NET Core构建RESTful API的技术指南

    译者荐语:利用周末的时间,本人拜读了长沙.NET技术社区翻译的技术标准<微软RESTFul API指南>,打算按照步骤写一个完整的教程,后来无意中看到了这篇文章,与我要写的主题有不少相似之 ...

  5. Asp.Net Core 5 REST API - Step by Step

    翻译自 Mohamad Lawand 2021年1月19日的文章 <Asp.Net Core 5 Rest API Step by Step> [1] 在本文中,我们将创建一个简单的 As ...

  6. Asp.Net Core 5 REST API 使用 JWT 身份验证 - Step by Step

    翻译自 Mohamad Lawand 2021年1月22日的文章 <Asp Net Core 5 Rest API Authentication with JWT Step by Step> ...

  7. Asp Net Core 5 REST API 使用 RefreshToken 刷新 JWT - Step by Step

    翻译自 Mohamad Lawand 2021年1月25日的文章 <Refresh JWT with Refresh Tokens in Asp Net Core 5 Rest API Step ...

  8. 温故知新,使用ASP.NET Core创建Web API,永远第一次

    ASP.NET Core简介 ASP.NET Core是一个跨平台的高性能开源框架,用于生成启用云且连接Internet的新式应用. 使用ASP.NET Core,您可以: 生成Web应用和服务.物联 ...

  9. 关于ASP.Net Core Web及API身份认证的解决方案

    6月15日,在端午节前的最后一个工作日,想起有段日子没有写过文章了,倒有些荒疏了.今借夏日蒸蒸之气,偷得浮生半日悠闲.闲话就说到这里吧,提前祝大家端午愉快(屈原听了该不高兴了:))!.NetCore自 ...

随机推荐

  1. 模型融合——stacking原理与实现

    一般提升模型效果从两个大的方面入手 数据层面:数据增强.特征工程等 模型层面:调参,模型融合 模型融合:通过融合多个不同的模型,可能提升机器学习的性能.这一方法在各种机器学习比赛中广泛应用, 也是在比 ...

  2. 虫师Selenium2+Python_2、测试环境搭建

    windows环境配置: 步骤: 安装python 官网下载http://www.seleniumhq.org/ https://www.python.org/downloads/windows/ 3 ...

  3. Linux基础:VLAN篇

    一.二层基础知识 1.1 vlan介绍 本小节重点: vlan的含义 vlan的类型 交换机端口类型 vlan的不足 1.1.1 vlan的含义 局域网LAN的发展是VLAN产生的基础,因而先介绍一下 ...

  4. visual studio自动向量化

    //////////////////////////////////////////////////*SSE 和 AVX 每个都有16个寄存器SSE 有 XMM0 ~ XMM15,是128bitAVX ...

  5. 使用docker部署canal

    文章目录 mysql开启binlog mysql创建canal用户 启动canal容器 配置canal 启动canal容器 查看docker容器日志 canal-client 验证 关于canal m ...

  6. Django创建的第一个项目(2)

    如何创建一个项目?安装好python,pycharm,Django之后,然后在pycharm的命令行django-admin  startproject   MyFirstPjt.MyFirstPjt ...

  7. python-通过configparser模块读取后缀为 .ini 的配置文件信息

    前言 一般为了方便会将路径,连接信息等写到配置文件(通常会将这些信息写到yaml,ini....配置文件)中,configparser模块读取后缀为 .ini 的配置文件信息 配置文件格式 #存在 c ...

  8. KVM 虚机镜像操作, 扩容和压缩

    KVM镜像操作 qemu-img命令 创建镜像 qemu-img create # 创建一个设备空间大小为10G的镜像 qemu-img create -f qcow2 centos7-guest.q ...

  9. 使用工具john破解系统密码

    下载解压得到一个存在着hash值的passwd的文件,还有一个压缩包解压得到的是一个密码本,应该就是需要使用爆破的密码本了 放在kali里面,根据题目的要求,将root的hash复制下来然后输入到一个 ...

  10. RFC3918组转发矩阵测试——网络测试仪实操

    一.简介 1.RFC3918简介 历史 · 在1999年3月成为正式标准 功能 · 评测网络互连设备或网络系统的性能 · 网络设备: 交换机,路由器- 内容 · 定义了一整套测试方法,为不同厂家的设备 ...