一題多解,ASP.NET Core應用啟動初始化的N種方案[下篇]

[接上篇]“天下大勢,分久必合,合久必分”,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。針對子環境的設置需要利用上述的承載配置來提供。

434b6a9289b2dbfe186cdddbb675e475.png

如下所示的就是上述三個配置文件的內容。如果當前環境和子環境分別為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>方法進行注冊的。程序啟動之后,利用瀏覽器的請求會得到如下圖所示的結果。

988e4a75ddfd6baa1e3e49eb647624ee.png

三、承載環境

承載環境(環境名稱、內容文件根目錄和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對象一旦被創建,承載環境就會固定下來,不能在改變。

f1308c4e093a1d41843f026dbb783cbc.png

可能有人還不死心,想到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的形式返回當前承載環境。程序運行之后,針對根路徑的請求會得到如下所示的輸出結果,可以看出利用配置對承載環境的設置并沒有生效。

9079ed14f3e6867f4e2b33939d8b036a.png

四、承載配置

承載配置會影響應用配置,比如針對演示實例的應用配置在設置的時候需要使用到對當前“子環境名稱”的設置。承載環境還會影響服務注冊,我們針對設置的“子環境”進行針對性的服務注冊也是一個常見的需求。由于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對象來完成。

70f56c6c0eaedf480e51e6a280c71618.png

八、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異常。

a1acfdce671315431c038f9029a4adbc.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281764.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281764.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281764.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java jpa 模糊查詢_JPA 以SQL實現分頁不模糊查詢(參數可能為空)

repository代碼:package com.fancy.miniflow.repository;import java.util.List;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.JpaSpecificationExecutor;import org.springframework.data.jpa.repository.Q…

GitHub服務中斷24小時11分鐘事故分析報告\n

上周&#xff0c;GitHub經歷了一次事故&#xff0c;導致服務降級24小時11分鐘。雖然平臺的某些部分不受事故影響&#xff0c;但仍然有多個內部系統受到了影響&#xff0c;向用戶顯示了過時且不一致的內容。所幸沒有用戶數據丟失&#xff0c;但針對幾秒鐘數據庫寫入的手動調整工…

8 旋轉數組的最小數字

輸入一個遞增排序數組的一個旋轉&#xff0c;輸出旋轉數組的最小元素例如1,2,3,4,5的一個旋轉可以為3,4,5,1,2把一個數組的最開始若干個元素搬到數組的末尾&#xff0c;稱之為數組的旋轉 輸出旋轉數組的最小元素 C: 1 class Solution {2 public:3 int minInOrder(vector<…

軟考新思維--2017年上半年信息系統項目管理師上午試題分析與答案(試題6-10題)...

2017年上半年信息系統項目管理師上午試題分析與答案&#xff08;試題1-5題&#xff09; 6.&#xff08;&#xff09;不是獲取需求的方法。A、問卷調查B、會議討論C、獲取原型D、決策分析【軟考新思維】需求是獲取的得來的&#xff0c;不是決策得來的。 先是獲取需求&#xff0c…

php 合并 字符串_PHP如何去重合并字符串

本篇文章主要給大家介紹PHP如何去重合并字符串。推薦教程&#xff1a;《PHP教程》對于PHP學習者來說&#xff0c;合并多個字符串&#xff0c;應該并不是很難。但是如果這多個字符串中&#xff0c;有相同元素&#xff0c;當我們想要合并他們并且要使其值具有唯一值。也就是說合并…

10.31T4 HAOI2010最長公共子序列 計數+容斥原理

2775 -- 【HAOI2010】最長公共子序列 Description 字符序列的子序列是指從給定字符序列中隨意地&#xff08;不一定連續&#xff09;去掉若干個字符&#xff08;可能一個也不去掉&#xff09;后所形成的字符序列。令給定的字符序列X“x0&#xff0c;x1&#xff0c;…&#xff0…

軟概(lesson 2):課堂測試

一、測試題目 二、完成過程 1.設計思想 ①連接mysql數據庫 ②設計user類&#xff0c;增加參數 ③設計add類&#xff0c;向數據庫內增加內容 ④設計addInput頁面&#xff0c;完成錄入操作 ⑤設計add頁面&#xff0c;接收錄入的參數&#xff0c;并調用add類函數 2.源代碼 user.ja…

谷歌Gboard輸入法新增“無痕模式”:僅在Chrome隱身窗口中適用

據外媒Android Police報道&#xff0c;如大家所知道的&#xff0c;Chrome瀏覽器中的“隱身模式”是為了防止你的私密瀏覽記錄被其他人看到&#xff0c;但是&#xff0c;在這種模式下&#xff0c;你的輸入法鍵盤依然會記住你輸入的短語&#xff0c;為了阻止你的鍵盤在Chrome隱身…

php兩個數組融合,php合并兩個數組的方式有哪些

1、arrary_merge示例代碼&#xff1a;$arr1 array(1, 2, 3, 4, 5);$arr2 array(1, 2, 6, 7, 8, 9, 10);$result1 array_merge($arr1, $arr2);$arr3 array("name" > "itbsl", "age" > 13, "sex" > "Male");$arr…

最近對latin-1這個字符集產生了不少好感

【簡介】 最近我要解析一個數據庫中間件的日志、這個中間件會在日志中記錄SQL發往的后臺DB ,執行耗時&#xff0c;對應的SQL&#xff1b;中間件直接把SQL寫到 了日志中去&#xff0c;并沒有對SQL進行適當的編碼轉換&#xff1b;理想情況下這個也不會有什么問題&#xff0c;不幸…

面象對象設計原則之六:迪米特原則(LeastKnowledge Principle, LKP)

迪米特法則來自于1987年美國東北大學(Northeastern University)一個名為“Demeter”的研究項目。迪米特法則又稱為最少知識原則(LeastKnowledge Principle, LKP)&#xff0c;其定義如下&#xff1a; 迪米特法則(Law of Demeter, LoD)&#xff1a;一個軟件實體應當盡可能少地與…

php symfony urlmatcher-gt;match,symfony路由組件(The Routing Component)

The Routing component 把HTTP request轉換為一系列的配置參數.安裝你有兩種方式來安裝這個組件:通過 Composer (symfony/routing on Packagist);使用官方的 Git repository (https://github.com/symfony/Routing)。然后, 需要Composer把vendor/autoload.php 這個文件提供 給 a…

R升級和包更新

1.R升級 # 安裝包"installr" install.packages("installr") # 導入包 library(installr) # 升級 updateR() 2.包升級 # 包升級 update.packages() 3.安裝包 # 選擇鏡像 options(reposstructure(c(CRAN"https://cran.cnr.berkeley.edu/"))) # 安裝…

其他對象的表單

1.textarea&#xff1a; textarea對象就想是input對象中的text樣式的表單&#xff0c;只不過是擴展過的text樣式表單。它可以通過行&#xff08;rows&#xff09;屬性和列&#xff08;cols&#xff09;屬性來編輯文本域的大小。最常見于留言板、論壇時回帖時的文本框等。 <h…

WinForm(十三)WebView2

WebView是WinForm框架中一個控件&#xff0c;用來對網頁信息交互&#xff0c;有時Web自己開發的&#xff0c;有時Web是三方的。下面通過一個例子來看看WebView2的使用。首先看Web的邏輯&#xff0c;是一個商品添加頁面&#xff0c;用AlpineJS和BootStrap來開發的&#xff0c;業…

Fluent UDF【4】:C語言

Fluent UDF利用的是C語言&#xff0c;本文簡單介紹在UDF中經常會用到的C語言常識。 本文部分內容來自UDF手冊。 1 C語言中的注釋 C語言中的注釋利用/*及*/來實現。例如: /*這是一個注釋*/ 注釋也可以跨行實現&#xff0c;如: /*這是一個 跨行注釋*/ 注意:在編寫UDF的過程中&…

java 畫磚塊,鋼筆畫入門:教你畫磚塊

說到磚塊很多朋友會想到搬磚&#xff0c;繪畫吧今天要教大家用鋼筆畫一塊磚&#xff0c;因為畫建筑的時候經常要畫磚墻&#xff0c;我們先從簡單的磚塊學起&#xff0c;之后繪畫吧會給大家分享畫一面磚墻的哦。繪制要點&#xff1a;本教程的主體物選擇了一塊有小殘缺面的磚頭。…

[轉] Node.js的線程和進程

[From] http://www.admin10000.com/document/4196.html 前言 很多Node.js初學者都會有這樣的疑惑&#xff0c;Node.js到底是單線程的還是多線程的&#xff1f;通過本章的學習&#xff0c;能夠讓讀者較為清晰的理解Node.js對于單/多線程的關系和支持情況。同時本章還將列舉一些讓…

第三方支付異步通知的陷阱

版權聲明&#xff1a;本文為博主原創文章&#xff0c;未經博主允許不得轉載。 https://blog.csdn.net/j16421881/article/details/78703792 用戶下單后調用第三方支付付款&#xff0c;然后接收第三方支付的異步通知&#xff0c;以便確認支付是否成功。 如下圖 但異步通知可能…

js請求php文件 302,采集某個 url, js 請求 200,瀏覽器訪問 302

/** 文件名: sso.js* 描述: 提供對 CAS 單點登錄的封裝** 功能說明&#xff1a;* 實現多個應用之間的單點登錄( SSO )功能&#xff0c;應用可以部署在不同的域名。容器的退出直接寫在頭里&#xff0c;避免 JS 過多加載** 版本: 1.0.0.1* 作者: [email protected]* 日期&#xf…