[接上篇]“天下大势,分久必合,合久必分”,ASP.NET应用通过GenericWebHostService这个承载服务被整合到基于IHostBuilder/IHost的服务承载系统中之后,也许微软还是意识到Web应用和后台服务的承载方式还是应该加以区分,于是推出了基于WebApplicationBuilder/WebApplication的承载方式。我们可以将其称为第三代承载模式,它有一个官方的名称叫做“Minimal API”。Minimal API同样面临向后兼容的问题,而且这次需要同时兼容前面两代承载模式,所以我们会发现“上篇”中提到的一系列初始化操作有了更多实现方式。[本文部分内容来源于《ASP.NET Core 6框架揭秘》第15章]

目录

一、Minimal API

二、推荐编程方式

三、承载环境

四、承载配置

五、应用配置

六、服务注册

七、中间件注册

八、Startup类型不再被支持

一、Minimal API

基于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种方案[下篇]

一题多解,ASP.NET Core应用启动初始化的N种方案[下篇]的更多相关文章

  1. 一题多解,ASP.NET Core应用启动初始化的N种方案[上篇]

    ASP.NET Core应用本质上就是一个由中间件构成的管道,承载系统将应用承载于一个托管进程中运行起来,其核心任务就是将这个管道构建起来.在ASP.NET Core的发展历史上先后出现了三种应用承载 ...

  2. 如何在ASP.NET Core程序启动时运行异步任务(3)

    原文:Running async tasks on app startup in ASP.NET Core (Part 3) 作者:Andrew Lock 译者:Lamond Lu 之前我写了两篇有关 ...

  3. 如何在ASP.NET Core程序启动时运行异步任务(1)

    原文:Running async tasks on app startup in ASP.NET Core (Part 1) 作者:Andrew Lock 译者:Lamond Lu 背景 当我们做项目 ...

  4. 在ASP.NET Core中构建路由的5种方法

    原文链接 :https://stormpath.com/blog/routing-in-asp-net-core 在ASP.NET Core中构建路由的5种方法 原文链接 :https://storm ...

  5. C#调用接口注意要点 socket,模拟服务器、客户端通信 在ASP.NET Core中构建路由的5种方法

    C#调用接口注意要点   在用C#调用接口的时候,遇到需要通过调用登录接口才能调用其他的接口,因为在其他的接口需要在登录的状态下保存Cookie值才能有权限调用, 所以首先需要通过调用登录接口来保存c ...

  6. ASP.NET Core 的启动和运行机制

    目录 ASP .NET Core 的运行机制 ASP .NET Core 的启动 ASP .NET Core 的管道和中间件 参考 ASP .NET Core 的运行机制 Web Server: AS ...

  7. ASP.NET Core 设置和初始化数据库 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 设置和初始化数据库 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 设置和初始化数据库 上一章节中我们已经设置和配置好了 EF ...

  8. [ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [下篇]

    由于ASP.NET Core框架在本质上就是由服务器和中间件构建的消息处理管道,所以在它上面构建的应用开发框架都是建立在某种类型的中间件上,整个ASP.NET Core MVC开发框架就是建立在用来实 ...

  9. [转][ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [下篇]

    由于ASP.NET Core框架在本质上就是由服务器和中间件构建的消息处理管道,所以在它上面构建的应用开发框架都是建立在某种类型的中间件上,整个ASP.NET Core MVC开发框架就是建立在用来实 ...

随机推荐

  1. STM32 DMA(UART)

    转载自https://blog.csdn.net/as480133937/article/details/104827639/

  2. [RPC学习]Dubbo+nacos实现动态更新内存RTree

    1.背景 服务架构一般都是从 单体架构 -> 微服务架构 -> 分布式架构 的迭代,我上一家公司就是在业务发展到一定规模时,开始拆老的单体服务,按业务维度拆成多个微服务,服务之间用的是HT ...

  3. python操作MySQL、事务、SQL注入问题

    python操作MySQL python中支持操作MySQl的模块很多 其中最常见就是'pymysql' # 属于第三方模块 pip3 install pymysql # 基本使用 import py ...

  4. Vue的vue-router基本使用

    一.Vue-router是什么? Vue Router是Vue.js的路由管理器.和Vue.js的核心深度集成,是SPA单页应用的路径管理器,适合用于构建单页面应用.Vue的单页面应用是基于路由和组件 ...

  5. spring boot 中接口参数为枚举时的反序列化配置(总结)

    步骤 如果是 GET 请求中需要反序列化枚举值(即 url 中的参数[querystring]),确保以下两点 1.1. 重写 StringToEnumConverterFactory 1.2. 配置 ...

  6. RabitMQ 简介

    每日一句 The secret of being miserable is to have leisure to bother about whether you are happy or not. ...

  7. [BJOI2014]想法

    参考 P4581传送门 题意:给DAG,问每个点可以由多少个叶子到达. 思路: 随机化!!(题面有提示) 这道题利用在一个范围内随机的数期望均分范围的性质. 直接每个叶子在\([0,Max\_Rand ...

  8. docker安装nginx,配置SSL

    nginx安装 下载镜像并测试 1.docker pull nginx 2.docker images nginx 查看我们拉取到本地的nginx镜像IMAGE ID 3.首先测试下nginx镜像是否 ...

  9. SQL Server 2017 各版本之间的差异

    SQL Server 2017的亮点 您选择的语言和平台 使用您选择的语言在本地和云中(现在在 Windows.Linux 和 Docker 容器上)构建现代应用程序. 行业领先的性能 充分利用任务关 ...

  10. 逻辑运算符——JavaSE基础

    逻辑运算符 运算符 说明 逻辑与 &( 与) 两个操作数为true,结果才是true,否则是false 逻辑或 |(或) 两个操作数有一个是true,结果就是true 短路与 &&am ...