[接上篇]“天下大势,分久必合,合久必分”,ASP.NET应用通过GenericWebHostService这个承载服务被整合到基于IHostBuilder/IHost的服务承载系统中之后,也许微软还是意识到Web应用和后台服务的承载方式还是应该加以区分,于是推出了基于WebApplicationBuilder/WebApplication的承载方式。我们可以将其称为第三代承载模式,它有一个官方的名称叫做“Minimal API”。Minimal API同样面临向后兼容的问题,而且这次需要同时兼容前面两代承载模式,所以我们会发现“上篇”中提到的一系列初始化操作有了更多实现方式。[本文部分内容来源于《ASP.NET Core 6框架揭秘》第15章]
一、Minimal API目录
一、Minimal API
二、推荐编程方式
三、承载环境
四、承载配置
五、应用配置
六、服务注册
七、中间件注册
八、Startup类型不再被支持
基于Minimal API的第三代应用承载方式的推出并非又回到了起点,因为底层的承载方式其实没有改变,它只是在上面再封装了一层而已。新的应用承载方式依然采用“构建者(Builder)”模式,核心的两个对象分别为WebApplication和WebApplicationBuilder,代表承载应用的WebApplication对象由WebApplicationBuilder对象进行构建。第二代承载模式需要提供针对IWebHostBuilder接口的兼容,作为第三代承载模式的Minimal API则需要同时提供针对IWebHostBuilder和IHostBuilder接口的兼容,此兼容性是通过这两个接口的实现类型ConfigureWebHostBuilder和ConfigureHostBuilder达成的。
WebApplicationBuilder类型的WebHost和Host属性返回了这两个对象,之前定义在IWebHostBuilder和IHostBuilder接口上的绝大部分API(并非所有API)借助它们得以复用。也正是有了这段历史,我们会发现相同的功能具有两到三种不同的编程方式。比如IWebHostBuilder和IHostBuilder接口上都提供了注册服务的方法,而WebApplicationBuilder类型利用Services属性直接将存放服务注册的IServiceCollection对象暴露出来,所以任何的服务注册都可以利用这个属性来完成。
public sealed class WebApplicationBuilder { public ConfigureWebHostBuilder WebHost { get; } public ConfigureHostBuilder Host { get; } public IServiceCollection Services { get; } public ConfigurationManager Configuration { get; } public ILoggingBuilder Logging { get; } public IWebHostEnvironment Environment { get; } public WebApplication Build(); } public sealed class ConfigureWebHostBuilder : IWebHostBuilder, ISupportsStartup public sealed class ConfigureHostBuilder : IHostBuilder, ISupportsConfigureWebHost
IWebHostBuilder和IHostBuilder接口都提供了设置配置和日志的方法,这两方面的设置都可以利用WebApplicationBuilder利用Configuration和Logging暴露出来的ConfigurationManager和ILoggingBuilder对象来实现。既然我们采用了Minimal API,那么我们就应该尽可能得使用WebApplicationBuilder类型提供的API。
二、推荐编程方式我们再次使用[上篇]提供的实例来演示承载配置、应用配置、承载环境、服务注册和中间件在Minimal API下的标准编程方式。该演示实例会注册如下这个FoobarMiddleware中间件,后者利用注入的IHandler服务完成请求的处理工作。作为IHandler接口的默认实现类型,Handler利用构造函数注入的IOptions<FoobarbazOptions>对象得到配置选项FoobarbazOptions,并将其内容作为请求的响应。
public class FoobarMiddleware { private readonly RequestDelegate _next; public FoobarMiddleware(RequestDelegate _) { } public Task InvokeAsync(HttpContext httpContext, IHandler handler) => handler.InvokeAsync(httpContext); } public interface IHandler { Task InvokeAsync(HttpContext httpContext); } public class Handler : IHandler { private readonly FoobarbazOptions _options; private readonly IWebHostEnvironment _environment; public Handler(IOptions<FoobarbazOptions> optionsAccessor, IWebHostEnvironment environment) { _options = optionsAccessor.Value; _environment = environment; } public Task InvokeAsync(HttpContext httpContext) { var payload = @$" Environment.ApplicationName: {_environment.ApplicationName} Environment.EnvironmentName: {_environment.EnvironmentName} Environment.ContentRootPath: {_environment.ContentRootPath} Environment.WebRootPath: {_environment.WebRootPath} Foo: {_options.Foo} Bar: {_options.Bar} Baz: {_options.Baz} "; return httpContext.Response.WriteAsync(payload); } } public class FoobarbazOptions { public string Foo { get; set; } = default!; public string Bar { get; set; } = default!; public string Baz { get; set; } = default!; }
我们会利用与当前“承载环境”对应配置来绑定配置选项FoobarbazOptions,后者的三个属性分别来源于三个独立的配置文件。其中settings.json被所有环境共享,settings.dev.json针对名为“dev”的开发环境。我们为承载环境提供更高的要求,在环境基础上进步划分子环境,settings.dev.dev1.json针对的就是dev下的子环境dev1。针对子环境的设置需要利用上述的承载配置来提供。
如下所示的就是上述三个配置文件的内容。如果当前环境和子环境分别为dev和dev1,那么配置选项FoobarbazOptions的内容将来源于这三个配置文件。细心的朋友可能还注意到了:我们并没有放在默认的根目录下,而是放在创建的resources目录下,这是因为我们需要利用针对承载环境的设置改变ASP.NET Core应用存放内容文件和Web资源文件的根目录。
settings.json { "Foo": "123" } settings.dev.json { "Bar": "abc" } settings.dev.dev1.json { "Baz": "xyz" }
如下所示的代码体现了承载配置、应用配置、承载环境、服务注册和中间件注册这五种初始化操作在Minimal API中的标准编程方式。与承载环境相关的承载配置(环境名称和内容文件与Web资源文件根目录)被定义在WebApplicationOptions配置选项上,并将其作为参数调用WebApplication的静态工厂方法CreateBuilder将WebApplicationBuilder对象构建出来。WebApplicationBuilder的Configuration属性返回一个ConfigurationManager对象,由于它同时实现了IConfigurationBuilder和IConfiguration接口,所以我们利用利用它来设置配置源,并且能够确保配置原提供的配置能够实时反映到这个对象上。从编程的角度来说,Minimal API不再刻意地区分承载配置和应用配置,因为针对它们的设置都由这个ConfigurationManager对象来完成。我们利用这个对象将表示“子环境名称”的承载配置进行了设置。
using App; var options = new WebApplicationOptions { Args = args, EnvironmentName = "dev", ContentRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources"), WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources", "web") }; var appBuilder = WebApplication.CreateBuilder(options); appBuilder.Configuration["SubEnvironment"] = "dev1"; appBuilder.Configuration .AddJsonFile(path: "settings.json", optional: false) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.json", optional: true) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.{appBuilder.Configuration["SubEnvironment"]}.json", optional: true); appBuilder.Services .AddSingleton<IHandler, Handler>() .Configure<FoobarbazOptions>(appBuilder.Configuration); var app = appBuilder.Build(); app.UseMiddleware<FoobarMiddleware>(); app.Run();
在完成了针对承载配置(含承载环境)的设置后,我们利用同一个ConfigurationManager对象完成针对应用配置的设置。具体来说,我们针对当前环境注册了三个对应的配置文件,定位配置文件的环境名称来源于WebApplicationBuilder的Environment属性返回的IWebHostEnvironment对象,而子环境则直接从ConfigurationManager对象中提取。
WebApplicationBuilder的Services属性返回用来存放服务注册的IServiceCollection对象,所以需要的服务注册直接添加到这个集合中就可以了。由于WebApplicationBuilder自身能够提供承载环境和配置,所以针对环境以及当前配置进行针对性的服务注册变得更加直接。我们利用这个IServiceCollection对象完成了针对IHandler/Handler的注册,以及将配置绑定到FoobarbazOptions配置选项上。
在此之后,我们调用WebApplicationBuilder的Build方法将代表Web应用的WebApplication对象构建出来。由于后者的类型实现了IApplicationBuilder接口,所以我们可以直接利用它来完成中间件的注册,我们自定义的FoobarMiddleware中间件就可以调用它的UseMiddleware<TMiddleware>方法进行注册的。程序启动之后,利用浏览器的请求会得到如下图所示的结果。
三、承载环境承载环境(环境名称、内容文件根目录和Web资源文件根目录)相关的承载配置在Minimal API只支持如下三种设置方式:
- 利用WebApplicationOptions(如我们提供的演示程序)
- 利用命令行参数
- 利用环境变量
我们按照如下的方式对演示程序进行了改写,摒弃了WebApplicationOptions配置选项,改用三个对应的环境变量。由于环境变量会默认作为配置源,所以自然也可以利用环境变量设置子环境名称。
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "dev"); Environment.SetEnvironmentVariable("ASPNETCORE_SUBENVIRONMENT", "dev1"); Environment.SetEnvironmentVariable("ASPNETCORE_CONTENTROOT", Path.Combine(Directory.GetCurrentDirectory(), "resources")); Environment.SetEnvironmentVariable("ASPNETCORE_WEBROOT", Path.Combine(Directory.GetCurrentDirectory(), "resources", "web")); var appBuilder = WebApplication.CreateBuilder(args); appBuilder.Configuration .AddJsonFile(path: "settings.json", optional: false) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.json", optional: true) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.{appBuilder.Configuration["SubEnvironment"]}.json", optional: true); appBuilder.Services .AddSingleton<IHandler, Handler>() .Configure<FoobarbazOptions>(appBuilder.Configuration); var app = appBuilder.Build(); app.UseMiddleware<FoobarMiddleware>(); app.Run();
由于WebApplicationBuilder利用WebHost属性提供的ConfigureWebHostBuilder(实现了IWebHostBuilder接口)对象来兼容原来定义在IWebHostBuilder接口上的API,有的人可以会觉得我们一定也能够像之前那样利用这个对象来设置承载环境,我们不妨来试试是否可行。如下面的代码片段所示,我们直接调用该对象的UseEnvironment、UseContentRoot和UseWebRoot方法对环境名称和内容文件与Web资源文件根目录进行了设置。
var appBuilder = WebApplication.CreateBuilder(args); appBuilder.WebHost .UseEnvironment("dev") .UseContentRoot(Path.Combine(Directory.GetCurrentDirectory(), "resources")) .UseWebRoot(Path.Combine(Directory.GetCurrentDirectory(), "resources", "web")); appBuilder.Configuration["SubEnvironment"] = "dev1"; appBuilder.Configuration .AddJsonFile(path: "settings.json", optional: false) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.json", optional: true) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.{appBuilder.Configuration["SubEnvironment"]}.json", optional: true); appBuilder.Services .AddSingleton<IHandler, Handler>() .Configure<FoobarbazOptions>(appBuilder.Configuration); var app = appBuilder.Build(); app.UseMiddleware<FoobarMiddleware>(); app.Run();
不幸的是,当我们启动程序之后会抛出如下所示的异常,并提示环境名称不能更改(其他承载环境属性也是一样),推荐使用WebApplicationOptions配置选项。由于承载环境是承载配置的范畴,但是Minimal API并没有刻意将两者区分开来,因为所有配置都实时体现在WebApplicationBuilder的Configuration属性返回的ConfigurationManager对象上。承载环境需要在最开始就被确定下来,因为后续后续配置的设置和服务注册都依赖于它,所以WebApplicationBuilder对象一旦被创建,承载环境就会固定下来,不能在改变。
可能有人还不死心,想到WebApplicationBuilder的Host属性不是还提供了一个ConfigureHostBuilder(实现了IHostBuilder接口)对象吗?我们是否可以按照如下的方式利用这个对象来设置承载环境相呢。很遗憾,我们同样会得到上面这个错误。
var appBuilder = WebApplication.CreateBuilder(args); appBuilder.Host .UseEnvironment("dev") .UseContentRoot(Path.Combine(Directory.GetCurrentDirectory(), "resources")); appBuilder.WebHost .UseWebRoot(Path.Combine(Directory.GetCurrentDirectory(), "resources", "web")); appBuilder.Configuration["SubEnvironment"] = "dev1"; appBuilder.Configuration .AddJsonFile(path: "settings.json", optional: false) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.json", optional: true) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.{appBuilder.Configuration["SubEnvironment"]}.json", optional: true); appBuilder.Services .AddSingleton<IHandler, Handler>() .Configure<FoobarbazOptions>(appBuilder.Configuration); var app = appBuilder.Build(); app.UseMiddleware<FoobarMiddleware>(); app.Run();
不论是IWebHostBuilder的UseEnvironment、UseContentRoot和UseWebRoot方法,还是IHostBuilder的UseEnvironment和UseContentRoot方法,它们最终都是对配置系统的更新,那么我们是否可以利用WebApplicationBuiler提供的ConfigurationManager对象按照如下的方式直接修改与承载环境相关的配置呢?
var appBuilder = WebApplication.CreateBuilder(args); appBuilder.Configuration["Environment"] = "dev"; appBuilder.Configuration["SubEnvironment"] = "dev1"; appBuilder.Configuration["ContentRoot"] = Path.Combine(Directory.GetCurrentDirectory(), "resources"); appBuilder.Configuration["WebRoot"] = Path.Combine(Directory.GetCurrentDirectory(), "resources","web"); var app = appBuilder.Build(); app.MapGet("/", (IWebHostEnvironment environment) => Results.Json(environment, new JsonSerializerOptions { WriteIndented = true})); app.Run();
在配置了与承载环境相关的几个属性之后,我们注册了一个针对根路径的路由,路由注册里会直接以JSON的形式返回当前承载环境。程序运行之后,针对根路径的请求会得到如下所示的输出结果,可以看出利用配置对承载环境的设置并没有生效。
四、承载配置承载配置会影响应用配置,比如针对演示实例的应用配置在设置的时候需要使用到对当前“子环境名称”的设置。承载环境还会影响服务注册,我们针对设置的“子环境”进行针对性的服务注册也是一个常见的需求。由于Minimal API将这两种类型的配置都集中到WebApplicationBuilder提供的ConfigurationManager对象上,所以针对承载配置的设置应该放在服务注册和设置应用配置之前。
由于WebApplicationBuilder可以为我们提供IWebHostBuilder和IHostBuilder,所以只要不涉及承载环境相关的几个预定义配置,其他承载配置(比如演示实例涉及的子环境名称)完全可以利用这两个对象进行设置。下面的代码片段演示了通过调用IWebHostBuilder的UseSettings方法来设置子环境名称。
var options = new WebApplicationOptions { Args = args, EnvironmentName = "dev", ContentRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources"), WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources", "web") }; var appBuilder = WebApplication.CreateBuilder(options); appBuilder.WebHost.UseSetting("SubEnvironment", "dev1"); appBuilder.Configuration .AddJsonFile(path: "settings.json", optional: false) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.json", optional: true) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.{appBuilder.Configuration["SubEnvironment"]}.json", optional: true); appBuilder.Services .AddSingleton<IHandler, Handler>() .Configure<FoobarbazOptions>(appBuilder.Configuration); var app = appBuilder.Build(); app.UseMiddleware<FoobarMiddleware>(); app.Run();
子环境名称同样可以按照如下的方式利用IHostBuilder的ConfigureHostConfiguration方法进行设置。
var options = new WebApplicationOptions { Args = args, EnvironmentName = "dev", ContentRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources"), WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources", "web") }; var appBuilder = WebApplication.CreateBuilder(options); appBuilder.Host.ConfigureHostConfiguration(config => config.AddInMemoryCollection( new Dictionary<string, string> { { "SubEnvironment" ,"dev1" } })); appBuilder.Configuration .AddJsonFile(path: "settings.json", optional: false) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.json", optional: true) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.{appBuilder.Configuration["SubEnvironment"]}.json", optional: true); appBuilder.Services .AddSingleton<IHandler, Handler>() .Configure<FoobarbazOptions>(appBuilder.Configuration); var app = appBuilder.Build(); app.UseMiddleware<FoobarMiddleware>(); app.Run();五、应用配置
Minimal API下针对应用配置的设置,最简单的方式莫过于上面演示的直接使用WebApplicationBuilder提供的ConfigurationManager对象。但是IWebHostBuilder和IHostBuilder接口的ConfigureAppConfiguration方法依然是可以使用的,所以演示实例针对应用配置的设置可以改写成如下两种形式。
var options = new WebApplicationOptions { Args = args, EnvironmentName = "dev", ContentRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources"), WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources", "web") }; var appBuilder = WebApplication.CreateBuilder(options); appBuilder.Host.ConfigureHostConfiguration(config => config.AddInMemoryCollection( new Dictionary<string, string> { { "SubEnvironment" ,"dev1" } })); appBuilder.WebHost.ConfigureAppConfiguration((context, config) => config .AddJsonFile(path: "settings.json", optional: false) .AddJsonFile(path: $"settings.{context.HostingEnvironment.EnvironmentName}.json", optional: true) .AddJsonFile(path: $"settings.{context.HostingEnvironment.EnvironmentName}.{context.Configuration["SubEnvironment"]}.json", optional: true)); appBuilder.Services .AddSingleton<IHandler, Handler>() .Configure<FoobarbazOptions>(appBuilder.Configuration); var app = appBuilder.Build(); app.UseMiddleware<FoobarMiddleware>(); app.Run();
var options = new WebApplicationOptions { Args = args, EnvironmentName = "dev", ContentRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources"), WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources", "web") }; var appBuilder = WebApplication.CreateBuilder(options); appBuilder.Host.ConfigureHostConfiguration(config => config.AddInMemoryCollection( new Dictionary<string, string> { { "SubEnvironment" ,"dev1" } })); appBuilder.Host.ConfigureAppConfiguration((context, config) => config .AddJsonFile(path: "settings.json", optional: false) .AddJsonFile(path: $"settings.{context.HostingEnvironment.EnvironmentName}.json", optional: true) .AddJsonFile(path: $"settings.{context.HostingEnvironment.EnvironmentName}.{context.Configuration["SubEnvironment"]}.json", optional: true)); appBuilder.Services .AddSingleton<IHandler, Handler>() .Configure<FoobarbazOptions>(appBuilder.Configuration); var app = appBuilder.Build(); app.UseMiddleware<FoobarMiddleware>(); app.Run();六、服务注册
既然WebApplicationBuilder的Services属性已经提供了用来存放服务注册的IServiceCollection对象,那么Minimal API下可以直接可以利用它来注册我们所需的服务。但是IWebHostBuilder和IHostBuilder接口的ConfigureServices方法依然是可以使用的,所以演示实例针对服务的注册可以改写成如下两种形式。
var options = new WebApplicationOptions { Args = args, EnvironmentName = "dev", ContentRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources"), WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources", "web") }; var appBuilder = WebApplication.CreateBuilder(options); appBuilder.Configuration["SubEnvironment"] = "dev1"; appBuilder.Configuration .AddJsonFile(path: "settings.json", optional: false) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.json", optional: true) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.{appBuilder.Configuration["SubEnvironment"]}.json", optional: true); appBuilder.WebHost.ConfigureServices((context, services) =>services .AddSingleton<IHandler, Handler>() .Configure<FoobarbazOptions>(context.Configuration)); var app = appBuilder.Build(); app.UseMiddleware<FoobarMiddleware>(); app.Run();
var options = new WebApplicationOptions { Args = args, EnvironmentName = "dev", ContentRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources"), WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources", "web") }; var appBuilder = WebApplication.CreateBuilder(options); appBuilder.Configuration["SubEnvironment"] = "dev1"; appBuilder.Configuration .AddJsonFile(path: "settings.json", optional: false) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.json", optional: true) .AddJsonFile(path: $"settings.{appBuilder.Environment.EnvironmentName}.{appBuilder.Configuration["SubEnvironment"]}.json", optional: true); appBuilder.Host.ConfigureServices((context, services) =>services .AddSingleton<IHandler, Handler>() .Configure<FoobarbazOptions>(context.Configuration)); var app = appBuilder.Build(); app.UseMiddleware<FoobarMiddleware>(); app.Run();七、中间件注册
中间件总是注册到IApplicationBuilder对象上,由于WebApplicationBuilder创建的WebApplication对象同时也是一个IApplicationBuilder对象,所以最简便快捷的中间件注册方法莫过于直接使用WebApplication对象。可能有人觉得也可以利用IWebHostBuiller的Configure方法来注册中间件,比如将我们的演示实例改写成如下的形式。
var options = new WebApplicationOptions { Args = args, EnvironmentName = "dev", ContentRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources"), WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources", "web") }; var appBuilder = WebApplication.CreateBuilder(options); appBuilder.Host.ConfigureHostConfiguration(config => config.AddInMemoryCollection( new Dictionary<string, string> { { "SubEnvironment" ,"dev1" } })); appBuilder.Host.ConfigureAppConfiguration((context, config) => config .AddJsonFile(path: "settings.json", optional: false) .AddJsonFile(path: $"settings.{context.HostingEnvironment.EnvironmentName}.json", optional: true) .AddJsonFile(path: $"settings.{context.HostingEnvironment.EnvironmentName}.{context.Configuration["SubEnvironment"]}.json", optional: true)); appBuilder.Services .AddSingleton<IHandler, Handler>() .Configure<FoobarbazOptions>(appBuilder.Configuration); appBuilder.WebHost.Configure(app => app.UseMiddleware<FoobarMiddleware>()); var app = appBuilder.Build(); app.Run();
实际上是不可以的,启动改写后的程序会抛出如下的NotSupportedException异常,并提示定义在WebApplicationBuilder的WenHost返回的ConfugureWebHostBuilder对象的Configure方法不再被支持,中间件的注册只能利用WebApplication对象来完成。
八、Startup类型不再被支持在Minimal API之前,将服务注册、中间件注册以及针对依赖注入容器的设置放在Startup类型中是一种被推荐的做法,但是这种编程方法在Minimal API中也不再被支持。
var options = new WebApplicationOptions { Args = args, EnvironmentName = "dev", ContentRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources"), WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "resources", "web") }; var appBuilder = WebApplication.CreateBuilder(options); appBuilder.Host.ConfigureHostConfiguration(config => config.AddInMemoryCollection( new Dictionary<string, string> { { "SubEnvironment" ,"dev1" } })); appBuilder.Host.ConfigureAppConfiguration((context, config) => config .AddJsonFile(path: "settings.json", optional: false) .AddJsonFile(path: $"settings.{context.HostingEnvironment.EnvironmentName}.json", optional: true) .AddJsonFile(path: $"settings.{context.HostingEnvironment.EnvironmentName}.{context.Configuration["SubEnvironment"]}.json", optional: true)); appBuilder.WebHost.UseStartup<Startup>(); var app = appBuilder.Build(); app.Run(); public class Startup { public Startup(IConfiguration configuration)=> Configuration = configuration; public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services .AddSingleton<IHandler, Handler>() .Configure<FoobarbazOptions>(Configuration); } public void Configure(IApplicationBuilder app)=>app.UseMiddleware<FoobarMiddleware>(); }
上面的程序将服务注册和中间件注册放在按照约定定义的Startup类型中,在利用WebApplicationBuilder的WebHost属性得到提供的ConfigureWebHostBuilder对象之后,我们调用其UseStartup方法对这个Startup类型进行了注册。遗憾的是,应用启动时同样会得到如下所示类似的NotSupportedException异常。
一题多解,ASP.NET Core应用启动初始化的N种方案[上篇]
一题多解,ASP.NET Core应用启动初始化的N种方案[下篇]