《ASP.NET Core 6框架揭秘》實例演示[27]:ASP.NET Core 6 Minimal API的模擬實現

Minimal API僅僅是在基于IHost/IHostBuilder的服務承載系統上作了小小的封裝而已,它利用WebApplication和WebApplicationBuilder這兩個類型提供了更加簡潔的API,同時提供了與現有API的兼容。[本文節選《ASP.NET Core 6框架揭秘》第17章]

一、基礎模型
二、WebApplication
三、WebApplication的構建
???? 1. BootstrapHostBuilder
???? 2. ConfigureHostBuilder
???? 3. ConfigureWebHostBuilder
???? 4. WebApplicationBuilder
四、 工廠方法

一、基礎模型

對于由WebApplication和WebApplicationBuilder構建的承載模型,我們沒有必要了解其實現的每一個細節,知道其大致的設計和實現原理就可以了,所以本節會采用最簡潔的代碼模擬這兩個類型的實現。如圖1所示,代表承載應用的WebApplication對象是對一個IHost對象的封裝,而且該類型自身也實現了IHost接口, WebApplication對象其實還是作為一個IHost對象被啟動的。作為構建這的WebApplicationBuilder則是對一個IHostBuilder對象的封裝,它對WebApplication對象的構建體現在利用封裝的IHostBuilder對象構建一個對應的IHost對象,最終利用后者將WebApplication對象創建出來。

177895a79fdf3f7aca2cd8e48e009e1f.png

圖17-8 完整的請求處理管道

二、WebApplication

WebApplication類型不僅僅實現了IHost接口,還同時實現IApplicationBuilder接口,所以中間件可以直接注冊到這個對象上的。該類型還實現了IEndpointRouteBuilder接口,所以我們還能利用它進行路由注冊,我們在20章才會涉及到路由,所以我們現在先忽略針對該接口的實現。下面的代碼模擬WebApplication類型的實現。如代碼片段所示,WebApplication的構造函數定義了一個IHost類型的參數,它利用這個對象完成了對IHost接口所有成員的實現,針對IApplicationBuilder接口成員的實現則利用創建的ApplicationBuilder對象來完成。WebApplication還提供了一個BuildRequestDelegate方法利用這個ApplicationBuilder對象完成了對中間件管道的構建。

public?class?WebApplication?:?IApplicationBuilder,?IHost
{private?readonly?IHost?_host;private?readonly?ApplicationBuilder?_app;public?WebApplication(IHost?host){_host?=?host;_app?=?new?ApplicationBuilder(host.Services);}IServiceProvider?IHost.Services?=>?_host.Services;Task?IHost.StartAsync(CancellationToken?cancellationToken)?=>?_host.StartAsync(cancellationToken);Task?IHost.StopAsync(CancellationToken?cancellationToken)?=>?_host.StopAsync(cancellationToken);IServiceProvider?IApplicationBuilder.ApplicationServices?{?get?=>?_app.ApplicationServices;?set?=>?_app.ApplicationServices?=?value;?}IFeatureCollection?IApplicationBuilder.ServerFeatures?=>?_app.ServerFeatures;IDictionary<string,?object?>?IApplicationBuilder.Properties?=>?_app.Properties;RequestDelegate?IApplicationBuilder.Build()?=>?_app.Build();IApplicationBuilder?IApplicationBuilder.New()?=>?_app.New();IApplicationBuilder?IApplicationBuilder.Use(Func<RequestDelegate,?RequestDelegate>?middleware)?=>?_app.Use(middleware);void?IDisposable.Dispose()?=>?_host.Dispose();public?IServiceProvider?Services?=>?_host.Services;internal?RequestDelegate?BuildRequestDelegate()?=>?_app.Build();...
}

WebApplication額外定義了如下的RunAsync和Run方法,它們分別以異步和同步方式啟動承載的應用。調用這兩個方法的時候可以指定監聽地址,指定的地址被添加到IServerAddressesFeature特性中,而服務器正式利用這個特性來提供監聽地址的。

public?class?WebApplication?:?IApplicationBuilder,?IHost
{private?readonly?IHost?_host;public?ICollection<string>?Urls?=>?_host.Services.GetRequiredService<IServer>().Features.Get<IServerAddressesFeature>()?.Addresses????throw?new?InvalidOperationException("IServerAddressesFeature?is?not?found.");public?Task?RunAsync(string??url?=?null){Listen(url);return?HostingAbstractionsHostExtensions.RunAsync(this);}public?void?Run(string??url?=?null){Listen(url);HostingAbstractionsHostExtensions.Run(this);}private?void?Listen(string??url){if?(url?is?not?null){var?addresses?=?_host.Services.GetRequiredService<IServer>().Features.Get<IServerAddressesFeature>()?.Addresses????throw?new?InvalidOperationException("IServerAddressesFeature?is?not?found.");addresses.Clear();addresses.Add(url);}}...
}

三、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對象上。

327520af23a4487458171c2f8ac9b559.png

圖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上下文。

public?class?BootstrapHostBuilder?:?IHostBuilder
{private?readonly?List<Action<IConfigurationBuilder>>?_configureHostConfigurations?=?new();private?readonly?List<Action<HostBuilderContext,?IConfigurationBuilder>>?_configureAppConfigurations?=?new();private?readonly?List<Action<HostBuilderContext,?IServiceCollection>>?_configureServices?=?new();private?readonly?List<Action<IHostBuilder>>?_others?=?new();public?IDictionary<object,?object>?Properties?{?get;?}?=?new?Dictionary<object,?object>();public?IHost?Build()?=>?throw?new?NotImplementedException();public?IHostBuilder?ConfigureHostConfiguration(Action<IConfigurationBuilder>?configureDelegate){_configureHostConfigurations.Add(configureDelegate);return?this;}public?IHostBuilder?ConfigureAppConfiguration(Action<HostBuilderContext,?IConfigurationBuilder>?configureDelegate){_configureAppConfigurations.Add(configureDelegate);return?this;}public?IHostBuilder?ConfigureServices(Action<HostBuilderContext,?IServiceCollection>?configureDelegate){_configureServices.Add(configureDelegate);return?this;}public?IHostBuilder?UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder>?factory){_others.Add(builder?=>?builder.UseServiceProviderFactory(factory));return?this;}public?IHostBuilder?UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext,?IServiceProviderFactory<TContainerBuilder>>?factory){_others.Add(builder?=>?builder.UseServiceProviderFactory(factory));return?this;}public?IHostBuilder?ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext,?TContainerBuilder>?configureDelegate){_others.Add(builder?=>?builder.ConfigureContainer(configureDelegate));return?this;}internal?void?Apply(IHostBuilder?hostBuilder,?ConfigurationManager?configuration,IServiceCollection?services,?out?HostBuilderContext?builderContext){//?初始化針對宿主的配置var?hostConfiguration?=?new?ConfigurationManager();_configureHostConfigurations.ForEach(?it?=>?it(hostConfiguration));//?創建承載環境var?environment?=?new?HostingEnvironment(){ApplicationName?=?hostConfiguration[HostDefaults.ApplicationKey],EnvironmentName?=?hostConfiguration[HostDefaults.EnvironmentKey]????Environments.Production,ContentRootPath?=?HostingPathResolver.ResolvePath(hostConfiguration[HostDefaults.ContentRootKey])};environment.ContentRootFileProvider?=?new?PhysicalFileProvider(environment.ContentRootPath);//?創建HostBuilderContext上下文var?hostContext?=?new?HostBuilderContext(Properties){Configuration?=?hostConfiguration,HostingEnvironment?=?environment,};//?將針對宿主的配置添加到ConfigurationManager中configuration.AddConfiguration(hostConfiguration,?true);//?初始化針對應用的配置_configureAppConfigurations.ForEach(?it?=>?it(hostContext,?configuration));//?收集服務注冊_configureServices.ForEach(?it?=>?it(hostContext,?services));//?將針對依賴注入容器的設置應用到指定的IHostBuilder對象上_others.ForEach(it?=>?it(hostBuilder));//?將自定義屬性轉移到指定的IHostBuilder對象上foreach?(var?kv?in?Properties){hostBuilder.Properties[kv.Key]?=?kv.Value;}builderContext?=?hostContext;}
}

除了Build方法,IHostBuilder接口中定義的所有方法的參數都是委托,所以實現的這些方法將提供的委托收集起來。在Apply方法中,我們通過執行這些委托對象,將初始化設置應用到指定的IServiceCollection、ConfigurationManager和IHostBuilder對象上,并根據初始化宿主配置構建出代表承載環境的HostingEnvironment對象。該方法最后根據承載環境結合配置將HostBuilderContext上下文創建出來,并以輸出參數的形式返回。

internal?static?class?HostingPathResolver
{public?static?string?ResolvePath(string??contentRootPath)?=>?ResolvePath(contentRootPath,?.BaseDirectory);public?static?string?ResolvePath(string??contentRootPath,?string?basePath)?=>?string.IsNullOrEmpty(contentRootPath)??Path.GetFullPath(basePath):?Path.IsPathRooted(contentRootPath)??Path.GetFullPath(contentRootPath):?Path.GetFullPath(Path.Combine(Path.GetFullPath(basePath),?contentRootPath));
}

2. ConfigureHostBuilder

ConfigureHostBuilder是在應用了BootstrapHostBuilder收集的初始化設置之后創建的,在創建該對象時提供了HostBuilderContext上下文, ConfigurationManager和IServiceCollection對象。提供的服務注冊直接添加到IServiceCollection對象中,針對配置的設置已經應用到ConfigurationManager對象,直接針對IHostBuilder對象的設置則利用_configureActions字段暫存起來。

public?class?ConfigureHostBuilder?:?IHostBuilder
{private?readonly?ConfigurationManager?_configuration;private?readonly?IServiceCollection?_services;private?readonly?HostBuilderContext?_context;private?readonly?List<Action<IHostBuilder>>?_configureActions?=?new();internal?ConfigureHostBuilder(HostBuilderContext?context,?ConfigurationManager?configuration,?IServiceCollection?services){_configuration?=?configuration;_services?=?services;_context?=?context;}public?IDictionary<object,?object>?Properties?=>?_context.Properties;public?IHost?Build()?=>?throw?new?NotImplementedException();public?IHostBuilder?ConfigureAppConfiguration(Action<HostBuilderContext,?IConfigurationBuilder>?configureDelegate)=>?Configure(()?=>?configureDelegate(_context,?_configuration));public?IHostBuilder?ConfigureHostConfiguration(Action<IConfigurationBuilder>?configureDelegate){var?applicationName?=?_configuration[HostDefaults.ApplicationKey];var?contentRoot?=?_context.HostingEnvironment.ContentRootPath;var?environment?=?_configuration[HostDefaults.EnvironmentKey];configureDelegate(_configuration);//?與環境相關的三個配置不允許改變Validate(applicationName,?HostDefaults.ApplicationKey,?"Application?name?cannot?be?changed.");Validate(contentRoot,?HostDefaults.ContentRootKey,?"Content?root?cannot?be?changed.");Validate(environment,?HostDefaults.EnvironmentKey,?"Environment?name?cannot?be?changed.");return?this;void?Validate(string?previousValue,?string?key,?string?message){if?(!string.Equals(previousValue,?_configuration[key],?StringComparison.OrdinalIgnoreCase)){throw?new?NotSupportedException(message);}}}public?IHostBuilder?ConfigureServices(Action<HostBuilderContext,?IServiceCollection>?configureDelegate)=>?Configure(()?=>?configureDelegate(_context,?_services));public?IHostBuilder?UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder>?factory)=>?Configure(()?=>?_configureActions.Add(b?=>?b.UseServiceProviderFactory(factory)));public?IHostBuilder?UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext,?IServiceProviderFactory<TContainerBuilder>>?factory)=>?Configure(()?=>?_configureActions.Add(b?=>?b.UseServiceProviderFactory(factory)));public?IHostBuilder?ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext,?TContainerBuilder>?configureDelegate)=>?Configure(()?=>?_configureActions.Add(b?=>?b.ConfigureContainer(configureDelegate)));private?IHostBuilder?Configure(Action?configure){configure();return?this;}internal?void?Apply(IHostBuilder?hostBuilder)?=>?_configureActions.ForEach(op?=>?op(hostBuilder));
}

WebApplicationBuilder對象一旦被創建出來后,針對承載環境的配置是不能改變的,所以ConfigureHostBuilder的ConfigureHostConfiguration方法針對此添加了相應的驗證。兩個UseServiceProviderFactory方法和ConfigureContainer方法針對依賴注入容器的設置最終需要應用到IHostBuilder對象上,所以我們將方法中提供的委托對象利用configureActions字段存起來,并最終利用Apply方法應用到指定的IHostBuilder對象上。

3. ConfigureWebHostBuilder

ConfigureWebHostBuilder同樣是在應用了BootstrapHostBuilder提供的初始化設置后創建的,創建該對象時能夠提供WebHostBuilderContext上下文和承載配置和服務注冊的ConfigurationManager和IServiceCollection對象。由于IWebHostBuilder接口定義的方法只涉及服務注冊和針對配置的設置,所以方法提供的委托對象可以直接應用到這兩個對象上。

public?class?ConfigureWebHostBuilder?:?IWebHostBuilder,?ISupportsStartup
{private?readonly?WebHostBuilderContext?_builderContext;private?readonly?IServiceCollection?_services;private?readonly?ConfigurationManager?_configuration;public?ConfigureWebHostBuilder(WebHostBuilderContext?builderContext,?ConfigurationManager?configuration,?IServiceCollection?services){_builderContext?=?builderContext;_services?=?services;_configuration?=?configuration;}public?IWebHost?Build()?=>?throw?new?NotImplementedException();public?IWebHostBuilder?ConfigureAppConfiguration(Action<WebHostBuilderContext,?IConfigurationBuilder>?configureDelegate)?=>?Configure(()?=>?configureDelegate(_builderContext,?_configuration));public?IWebHostBuilder?ConfigureServices(Action<IServiceCollection>?configureServices)?=>?Configure(()?=>?configureServices(_services));public?IWebHostBuilder?ConfigureServices(Action<WebHostBuilderContext,?IServiceCollection>?configureServices)?=>?Configure(()?=>?configureServices(_builderContext,?_services));public?string??GetSetting(string?key)?=>?_configuration[key];public?IWebHostBuilder?UseSetting(string?key,?string??value)?=>?Configure(()?=>?_configuration[key]?=?value);IWebHostBuilder?ISupportsStartup.UseStartup(Type?startupType)?=>?throw?new?NotImplementedException();IWebHostBuilder?ISupportsStartup.UseStartup<TStartup>(Func<WebHostBuilderContext,?TStartup>?startupFactory)?=>?throw?new?NotImplementedException();IWebHostBuilder?ISupportsStartup.Configure(Action<IApplicationBuilder>?configure)?=>?throw?new?NotImplementedException();IWebHostBuilder?ISupportsStartup.Configure(Action<WebHostBuilderContext,?IApplicationBuilder>?configure)?=>?throw?new?NotImplementedException();private?IWebHostBuilder?Configure(Action?configure){configure();return?this;}
}

我們在前面說過,傳統承載方式將初始化操作定義在注冊的Startup類型的編程方式在Minima API中已經不再被支持了,所以WebApplicationBuilder本不該實現ISupportsStartup接口,但是我們希望用戶在采用這種編程方式時得到顯式的提醒,所以依然讓它實現該接口,并在實現的方法中拋出NotImplementedException類型的異常。

4. WebApplicationBuilder

如下的代碼片段模擬了WebApplicationBuilder針對WebApplication的構建。它的構造函數會創建一個BootstrapHostBuilder對象,調用它的ConfigureDefaults和ConfigureWebHostDefaults擴展方法將初始化設置收集起來。ConfigureWebHostDefaults方法會利用提供的Action<IWebHostBuilder>委托進行中間件的注冊,由于中間件的注冊被轉移到WebApplication對象上,并且它提供了一個BuildRequestDelegate方法返回由注冊中間件組成的管道,所以在這里只需調用構建的WebApplication對象(通過_application字段表示,雖然此時尚未創建,但是中間件真正被注冊時會被創建出來)的這個方法,并將返回的RequestDelegate對象作為參數調用IApplicationBuilder接口的Run方法將中間件管道注冊為請求處理器。

public?class?WebApplicationBuilder
{private?readonly?HostBuilder?_hostBuilder?=?new?HostBuilder();private?WebApplication?_application;public?ConfigurationManager?Configuration?{?get;?}??=?new?ConfigurationManager();public?IServiceCollection?Services?{?get;?}??=?new?ServiceCollection();public?IWebHostEnvironment?Environment?{?get;?}public?ConfigureHostBuilder?Host?{?get;?}public?ConfigureWebHostBuilder?WebHost?{?get;?}public?ILoggingBuilder?Logging?{?get;?}public?WebApplicationBuilder(WebApplicationOptions?options){//創建BootstrapHostBuilder并利用它收集初始化過程中設置的配置、服務和針對依賴注入容器的設置var?args?=?options.Args;var?bootstrap?=?new?BootstrapHostBuilder();bootstrap.ConfigureDefaults(null).ConfigureWebHostDefaults(webHostBuilder?=>?webHostBuilder?.Configure(app?=>?app.Run(_application.BuildRequestDelegate()))).ConfigureHostConfiguration(config?=>?{//?添加命令行配置源if?(args?.Any()?==?true){config.AddCommandLine(args);}//?將WebApplicationOptions配置選項轉移到配置中Dictionary<string,?string>??settings?=?null;if?(options.EnvironmentName?is?not?null)??(settings???=?new())[HostDefaults.EnvironmentKey]???=?options.EnvironmentName;if?(options.ApplicationName?is?not?null)???(settings???=?new())[HostDefaults.ApplicationKey]???=?options.ApplicationName;if?(options.ContentRootPath?is?not?null)???(settings???=?new())[HostDefaults.ContentRootKey]???=?options.ContentRootPath;if?(options.WebRootPath?is?not?null)???(settings???=?new())[WebHostDefaults.WebRootKey]???=?options.EnvironmentName;if?(settings?!=?null){config.AddInMemoryCollection(settings);}});//?將BootstrapHostBuilder收集到配置和服務轉移到Configuration和Services上//?將應用到BootstrapHostBuilder上針對依賴注入溶質的設置轉移到_hostBuilder上//?得到BuilderContext上下文bootstrap.Apply(_hostBuilder,?Configuration,??Services,?out?var?builderContext);//?如果提供了命令行參數,在Configuration上添加對應配置源if?(options.Args?.Any()?==?true){Configuration.AddCommandLine(options.Args);}//?構建WebHostBuilderContext上下文//?初始化Host、WebHost和Logging屬性var?webHostContext?=?(WebHostBuilderContext)builderContext??.Properties[typeof(WebHostBuilderContext)];Environment?=?webHostContext.HostingEnvironment;Host?=?new?ConfigureHostBuilder(?builderContext,?Configuration,?Services);WebHost?=?new?ConfigureWebHostBuilder(?webHostContext,?Configuration,?Services);Logging?=?new?LogginigBuilder(Services);}public?WebApplication?Build(){//?將ConfigurationManager的配置轉移到_hostBuilder_hostBuilder.ConfigureAppConfiguration(builder?=>{builder.AddConfiguration(Configuration);foreach?(var?kv?in??((IConfigurationBuilder)Configuration).Properties){builder.Properties[kv.Key]?=?kv.Value;}});//?將添加的服務注冊轉移到_hostBuilder_hostBuilder.ConfigureServices((_,?services)?=>{foreach?(var?service?in?Services){services.Add(service);}});//?將應用到Host屬性上的設置轉移到_hostBuilderHost.Apply(_hostBuilder);//?利用_hostBuilder構建的IHost對象創建WebApplicationreturn?_application?=?new?WebApplication(_hostBuilder.Build());}
}

接下來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方法。

public?sealed?class?WebApplication
{public?static?WebApplicationBuilder?CreateBuilder()?=>?new?WebApplicationBuilder(new?WebApplicationOptions());public?static?WebApplicationBuilder?CreateBuilder(string[]?args){var?options?=?new?WebApplicationOptions();options.Args?=?args;return?new?WebApplicationBuilder(options);}public?static?WebApplicationBuilder?CreateBuilder(WebApplicationOptions?options)?=>?new?WebApplicationBuilder(options,?null);public?static?WebApplication?Create(string[]??args?=?null){var?options?=?new?WebApplicationOptions();options.Args?=?args;return?new?WebApplicationBuilder(options).Build();}
}

本節內容通過針對WebApplication和WebApplicationBuilder這兩個類型的實現模擬來講解Minimal API的實現原理。一方面為了讓講解更加清晰,另一方面也出于篇幅的限制,不得不省去很多細枝末節的內容,但是設計思想和實現原理別無二致。上面提供的源代碼也不是偽代碼,如下所示的就是在“模擬的Minimal API”構建的ASP.NET Core應用,它是可以正常運行的。如果讀者朋友們對真實的實現感興趣,可以將它作為一個“向導”去探尋“真實的Minimal API”。

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

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

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

相關文章

Mysql的關聯查詢語句

一 內連接( inner join&#xff09; 1、多表中同時符合某種條件的數據記錄的集合 (取兩表公共部分) 2、inner join 可以縮寫成 join 例如: select * from A,B WHERE A.idB.id 或者 select * from A inner join B on A.idB.id 內連接分為三類:{ &#xff08;1&#xff0…

高性能Server---Reactor模型

無處不在的C/S架構 在這個充斥著云的時代,我們使用的軟件可以說99%都是C/S架構的&#xff01; 你發郵件用的Outlook,Foxmail等你看視頻用的優酷&#xff0c;土豆等你寫文檔用的Office365,googleDoc&#xff0c;Evernote等你瀏覽網頁用的IE,Chrome等(B/S是特殊的C/S)……C/S架構…

計算機控制系統的試題,計算機控制系統練習題(1)

21. 給出多通道復用一個D/A轉換器的原理示意圖。 答&#xff1a;22. 什么是信號重構&#xff1f;答&#xff1a;把離散信號變為連續信號的過程&#xff0c;稱為信號重構&#xff0c;它是采樣的逆過程。23. 寫出零階保持器的傳遞函數&#xff0c;引入零階保持器對系統開環傳遞函…

springmvc_3(將數據放入map中)

jsp頁面 結果 轉載于:https://www.cnblogs.com/mohehpc/p/6491376.html

怎樣用原生js配合css的transition寫個無縫滾動

之所以想要寫原生js配合css轉換的無縫滾動&#xff0c;是因為之前在簡書上看到一哥們寫的一篇文章&#xff0c;說是在網上找了一堆js配合css transition屬性寫的輪播插件&#xff0c;可惜沒有無縫的效果&#xff0c;結果他用原生js重寫了一個可以無縫滾動的。好吧&#xff0c;我…

聊聊策略模式

1、簡介策略模式就是把各個平等的具體實現進行抽象、封裝成為獨立的算法類&#xff0c;然后通過上下文和具體的算法類來進行交互。各個策略算法都是平等的&#xff0c;地位是一樣的&#xff0c;正是由于各個算法的平等性&#xff0c;所以它們才是可以相互替換的。雖然我們可以動…

小學計算機課每周幾節,小學信息技術課時多少

滿意答案小學信息技術課程標準一、課程任務和教學目標中小學信息技術課程的主要任務是&#xff1a;培養學生對信息技術的興趣和意識&#xff0c;讓學生了解和掌握信息技術基本知識和技能&#xff0c;了解信息技術的發展及其應用對人類日常生活和科學技術的深刻影響。通過信息技…

張旭升20162329 2006-2007-2 《Java程序設計》第一周學習總結

20162329 2006-2007-2 《Java程序設計》第一周學習總結 教材學習內容總結 通過打書上的代碼熟悉了Java編程的基本過程 教材學習中的問題和解決過程 1.因為我的虛擬機不可用所以我在Windows中安裝了bash和git&#xff0c;但是由于Windows下bash中沒有中文而且我英語又不是很好所…

《圖解 HTTP》讀書筆記(未完待續)

ARP 協議&#xff08;Address Resolution Protocol&#xff09;一種以解析地址的協議&#xff0c;根據通信雙方的 IP 地址就可以查出對應的 MAC 地址。MAC&#xff08; Media Access Control Address&#xff09;地址是指網卡所屬的固定的地址MIME&#xff0c;多部分對象集合&a…

SQL查詢的安全方案

1.使用預處理語句防sql注入 2.寫入數據庫的數據要進行特殊字符轉義 3.錯誤信息不返回給用戶,記錄到日志 4.定期做數據備份 5.不給查詢用戶root權限,合理分配權限 6.關閉遠程訪問數據庫權限 7.修改root口令,不使用默認口令,使用較復雜口令 8.刪除多余的用戶 9.改變root用戶的名稱…

.NET 實現啟動時重定向程序運行路徑及 Windows 服務運行模式部署

日常工作中有時候會遇到需要將程序直接在服務器上運行&#xff0c;而不依賴于 IIS 托管的情況&#xff0c;直接運行有兩種方式&#xff0c;一種是部署為 服務模式&#xff0c;另一種則是 直接啟動 .NET 發布之后的 exe 文件以 控制臺模式運行&#xff0c;控制臺模式運行主要問題…

iOS runtime實戰應用:關聯對象

在開始之前建議先閱讀iOS runtime的基礎理解篇&#xff1a;iOS內功篇&#xff1a;runtime 有筒子在面試的時候&#xff0c;遇到這樣一個問題&#xff1a;“如何給NSArray添加一個屬性&#xff08;不能使用繼承&#xff09;”&#xff0c;筒子立馬蒙逼了&#xff0c;不能用繼承&…

黑龍江科技大學計算機考研復試科目,2020年黑龍江科技大學計算機應用技術考研經驗分享...

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓育明考研備考策略隨著IT業的迅猛發展&#xff0c;各高校計算機專業報名火爆&#xff0c;甚至文科學生跨專業報考時都會選擇計算機。計算機專業競爭日趨激烈&#xff0c;那么如何在充分發揮公共科目優勢的同時&#xff0c;盡量縮小專…

Mysql數據庫安全性問題【防注入】

一、SQL注入實例 后臺的插入語句代碼&#xff1a; $unsafe_variable $_POST[user_input]; mysql_query("INSERT INTO table (column) VALUES ($unsafe_variable)"); 當POST的內容為&#xff1a; value); DROP TABLE table;--以上的整個SQL查詢語句變成&#xff1…

Unexpected end of JSON input while parsing near錯誤解決方式(網上的方法)

原本是想創建一個create-react-app來著&#xff0c;但是在創建的中間會出現Unexpected end of JSON input while parsing near... 的錯誤。 在網上找到了一些方法&#xff0c;首先是清空npm的緩存。 npm cache clean --force 氮素&#xff0c;然并卵。near后面的內容變化了一下…

解決Qt5 Creator無法切換輸入法(fcitx),Ubuntu中不能使用搜狗輸入法錄入漢字問題...

2016年6月8日修正&#xff0c;ubuntu 16.04 Qt5.7.0 以及 Qt5.6.1均測試通過在Qt5.3之前&#xff0c;我發布過解決辦法 解決Qt5 Creator無法切換輸入法&#xff08;fcitx&#xff09;&#xff0c;不能錄入漢字問題&#xff0c;Qt5.4以及Qt5.5&#xff0c;舊辦法失效&#xff0c…

目前市場上用于個人計算機的硬盤尺寸是,第5章-硬盤(計算機組裝與維護).docx

ADDIN CNKISM.UserStyle一、選擇題1.磁盤存儲器的主要技術指標有多項&#xff0c;下面不屬于硬盤指標的是( )。A.存儲容量B.單碟容量C.轉速D.帶寬2.硬盤的平均尋道時間通常以毫秒為單位測量&#xff0c;是指( )。A.磁頭從一個柱面移到另一個隨機距離遠的柱面所需的平均時間B.…

Xmemcached學習筆記一(安裝memcached)

memcached有三種java客戶端 第一種&#xff1a;Com.danga 包下面的memcached&#xff0c;需引入jar(本人用的是memcached-2.5.2.jar 文末附上附件需要的可以下載) 第二種&#xff1a;spyMemcached 第三種&#xff1a;XMemcached 據說第三種是使用最簡單&#xff0c;最好用的&a…

WrapPanel 實現虛擬化

WrapPanel 實現虛擬化控件名&#xff1a;VirtualizingWrapPanel作者&#xff1a;WPFDevelopersOrg原文鏈接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用大于等于.NET40&#xff1b;Visual Studio 2022;項目使用 MIT 開源許可協議&#xff1b;眾…

pdo連接mysql數據庫(簡潔明了)

一 實例化pdo對象 $dsn "mysql:dbnametest;host127.0.0.1"; $pdo new PDO($dsn,root,root);二 數據查詢 1、如果不根據用戶傳過來的值進行操作,可以直接query sql $dsn "mysql:dbnametest;host127.0.0.1"; $pdo new PDO($dsn,root,root); $sql &qu…