日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

源码里没有configure_深入源码理解.NET Core中Startup的注册及运行

發布時間:2025/3/11 asp.net 57 豆豆
生活随笔 收集整理的這篇文章主要介紹了 源码里没有configure_深入源码理解.NET Core中Startup的注册及运行 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

開發.NET Core應用,直接映入眼簾的就是Startup類和Program類,它們是.NET Core應用程序的起點。通過使用Startup,可以配置化處理所有向應用程序所做的請求的管道,同時也可以減少.NET應用程序對單一服務器的依賴性,使我們在更大程度上專注于面向多服務器為中心的開發模式。

目錄:

  • Startup討論

    • Starup所承擔的角色

    • Startup編寫規范

    • ConfigureServices

    • Configure

    • 擴展Startup方法

  • 深入源碼查看Startup是如何注冊和執行的

    • UseStartup源碼

    • 創建Startup實例

    • ConfigureServices和Configure

Starup所承擔的角色

Startup類是ASP.NET Core程序中所必須的,可以使用多種修飾符(public、protect,private、internal),作為ASP.NET Core應用程序的入口,它包含與應用程序相關配置的功能或者說是接口。

雖然在程序里我們使用的類名就是Startup,但是需要注意的是,Startup是一個抽象概念,你完全可以名稱成其他的,比如MyAppStartup或者其他的什么名稱,只要你在Program類中啟動你所定義的啟動類即可。

以下是基于ASP.NET Core Preview 3模板中提供的寫法:

1: public class Program 2: { 3: public static void Main(string[] args) 4: { 5: CreateHostBuilder(args).Build().Run(); 6: } 7: ? 8: public static IHostBuilder CreateHostBuilder(string[] args) => 9: Host.CreateDefaultBuilder(args) 10: .ConfigureWebHostDefaults(webBuilder => 11: { 12: webBuilder.UseStartup(); 13: }); 14: }

不管你命名成什么,只要將webBuilder.UseStartup<>()中的泛型類配置成你定義的入口類即可;

Startup編寫規范

下面是ASP.NET Core 3.0 Preview 3模板中Startup的寫法:

1: // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 2: public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 3: { 4: if (env.IsDevelopment()) 5: { 6: app.UseDeveloperExceptionPage(); 7: } 8: else 9: { 10: // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 11: app.UseHsts(); 12: } 13: ? 14: app.UseHttpsRedirection(); 15: ? 16: app.UseRouting(routes => 17: { 18: routes.MapControllers(); 19: }); 20: ? 21: app.UseAuthorization(); 22: }

通過以上代碼可以知道,Startup類中一般包括

  • 構造函數:通過我們以前的開發經驗,我們可以知道,該構造方法可以包括多個對象

    • IConfiguration:表示一組鍵/值應用程序配置屬性。

    • IApplicationBuilder:是一個包含與當前環境相關的屬性和方法的接口。它用于獲取應用程序中的環境變量。

    • IHostingEnvironment:是一個包含與運行應用程序的Web宿主環境相關信息的接口。使用這個接口方法,我們可以改變應用程序的行為。

    • ILoggerFactory:是為ASP.NET Core中的日志記錄系統提供配置的接口。它還創建日志系統的實例。

  • ConfigureServices

  • Configure

Startup在創建服務時,會執行依賴項注冊服務,以便在應用程序的其它地方使用這些依賴項。ConfigureServices 用于注冊服務,Configure 方法允許我們向HTTP管道添加中間件和服務。這就是ConfigureServices先于Configure 之前調用的原因。

ConfigureServices

該方法時可選的,非強制約束,它主要用于對依賴注入或ApplicationServices在整個應用中的支持,該方法必須是public的,其典型模式是調用所有?Add{Service} 方法,主要場景包括實體框架、認證和 MVC 注冊服務:

1: services.AddDbContext(options =>options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); 2: services.AddDefaultIdentity().AddDefaultUI(UIFramework.Bootstrap4).AddEntityFrameworkStores(); 3: services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); 4: // Add application services.此處主要是注冊IOC服務 5: services.AddTransient(); 6: services.AddTransient();

Configure

該方法主要用于定義應用程序對每個HTTP請求的響應方式,即我們可以控制ASP.NET管道,還可用于在HTTP管道中配置中間件。請求管道中的每個中間件組件負責調用管道中的下一個組件,或在適當情況下使鏈發生短路。 如果中間件鏈中未發生短路,則每個中間件都有第二次機會在將請求發送到客戶端前處理該請求。

該方法接受IApplicationBuilder作為參數,同時還可以接收其他一些可選參數,如IHostingEnvironment和ILoggerFactory。

一般而言,只要將服務注冊到configureServices方法中時,都可以在該方法中使用。

1: app.UseDeveloperExceptionPage(); 2: app.UseHsts(); 3: app.UseHttpsRedirection(); 4: app.UseRouting(routes => 5: { 6: routes.MapControllers(); 7: }); 8: app.UseAuthorization();

擴展Startup方法

使用IStartupFilter來對Startup功能進行擴展,在應用的Configure中間件管道的開頭或末尾使用IStartupFilter來配置中間件。IStartupFilter有助于確保當庫在應用請求處理管道的開端或末尾添加中間件的前后運行中間件。

以下是IStartupFilter的源代碼,通過源代碼我們可以知道,該接口有一個Action類型,并命名為Configure的方法。由于傳入參數類型和返回類型一樣,這就保證了擴展的傳遞性及順序性,具體的演示代碼,可以參數MSDN

1: using System; 2: using Microsoft.AspNetCore.Builder; 3: 4: namespace Microsoft.AspNetCore.Hosting 5: { 6: public interface IStartupFilter 7: { 8: Action Configure(Action next); 9: } 10: }

此段文字,只是我想深入了解其內部機制而寫的,如果本身也不了解,其實是不影響我們正常編寫.NET Core應用的。

UseStartup源碼

ASP.NET Core通過調用IWebHostBuilder.UseStartup方法,傳入Startup類型,注意開篇就已經說過Startup是一個抽象概念,我們看下源代碼:

1: /// 2: /// Specify the startup type to be used by the web host. 3: /// 4: /// The to configure. 5: /// The to be used. 6: /// The . 7: public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType) 8: { 9: var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name; 10: 11: hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName); 12: 13: // Light up the GenericWebHostBuilder implementation 14: if (hostBuilder is ISupportsStartup supportsStartup) 15: { 16: return supportsStartup.UseStartup(startupType); 17: } 18: 19: return hostBuilder 20: .ConfigureServices(services => 21: { 22: if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo())) 23: { 24: services.AddSingleton(typeof(IStartup), startupType); 25: } 26: else 27: { 28: services.AddSingleton(typeof(IStartup), sp => 29: { 30: var hostingEnvironment = sp.GetRequiredService(); 31: return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName)); 32: }); 33: } 34: }); 35: } 36: 37: /// 38: /// Specify the startup type to be used by the web host. 39: /// 40: /// The to configure. 41: /// The type containing the startup methods for the application. 42: /// The . 43: public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder) where TStartup :class 44: { 45: return hostBuilder.UseStartup(typeof(TStartup)); 46: }

創建Startup實例

1: /// 2: /// Adds a delegate for configuring additional services for the host or web application. This may be called 3: /// multiple times. 4: /// 5: /// A delegate for configuring the . 6: /// The . 7: public IWebHostBuilder ConfigureServices(Action configureServices) 8: { 9: if (configureServices == null) 10: { 11: throw new ArgumentNullException(nameof(configureServices)); 12: } 13: ? 14: return ConfigureServices((_, services) => configureServices(services)); 15: } 16: ? 17: /// 18: /// Adds a delegate for configuring additional services for the host or web application. This may be called 19: /// multiple times. 20: /// 21: /// A delegate for configuring the . 22: /// The . 23: public IWebHostBuilder ConfigureServices(Action configureServices) 24: { 25: _configureServices += configureServices; 26: return this; 27: }

關于ConfigureServices的定義及注冊方式,是在IWebHostBuilder.ConfigureServices實現的,同時可以注意一下25行代碼,向大家說明了多次注冊Startup的ConfigureServices方法時,會合并起來的根源。此處抽象委托用的也非常多。

該類里面還有Build方法,我就不貼出代碼了,只需要知道,主進程在此處開始了。接下來一個比較重要的方法,是BuildCommonServices,它向當前ServiceCollection中添加一些公共框架級服務,以下是部分代碼,具體代碼請查看WebHostBuilder。

1: try 2: { 3: var startupType = StartupLoader.FindStartupType(_options.StartupAssembly, _hostingEnvironment.EnvironmentName); 4: ? 5: if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo())) 6: { 7: services.AddSingleton(typeof(IStartup), startupType); 8: } 9: else 10: { 11: services.AddSingleton(typeof(IStartup), sp => 12: { 13: var hostingEnvironment = sp.GetRequiredService(); 14: var methods = StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName); 15: return new ConventionBasedStartup(methods); 16: }); 17: } 18: } 19: catch (Exception ex) 20: { 21: var capture = ExceptionDispatchInfo.Capture(ex); 22: services.AddSingleton(_ => 23: { 24: capture.Throw(); 25: return null; 26: }); 27: }由此可見,如果我們的Startup類直接實現IStartup,它可以并且將直接注冊為IStartup的實現類型。只不過ASP.NET Core模板代碼并沒有實現IStartup,它更多的是一種約定,并通過DI調用委托,依此調用Startup內的構造函數還有另外兩個方法。同時上述代碼還展示了如何創建Startup類型,就是用到了靜態方法StartupLoader.LoadMethods類生成StartupMethods實例。

ConfigureServicesConfigure

當WebHost初始化時,框架會去查找相應的方法,這里,我們主要查看源代碼,其中的核心方法是StartupLoader.FindMethods 1: private static MethodInfo FindMethod(Type startupType, string methodName, string environmentName, Type returnType = null, bool required = true) 2: { 3: var methodNameWithEnv = string.Format(CultureInfo.InvariantCulture, methodName, environmentName); 4: var methodNameWithNoEnv = string.Format(CultureInfo.InvariantCulture, methodName, ""); 5: ? 6: var methods = startupType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 7: var selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithEnv, StringComparison.OrdinalIgnoreCase)).ToList(); 8: if (selectedMethods.Count > 1) 9: { 10: throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithEnv)); 11: } 12: if (selectedMethods.Count == 0) 13: { 14: selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithNoEnv, StringComparison.OrdinalIgnoreCase)).ToList(); 15: if (selectedMethods.Count > 1) 16: { 17: throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithNoEnv)); 18: } 19: } 20: ? 21: var methodInfo = selectedMethods.FirstOrDefault(); 22: if (methodInfo == null) 23: { 24: if (required) 25: { 26: throw new InvalidOperationException(string.Format("A public method named '{0}' or '{1}' could not be found in the '{2}' type.", 27: methodNameWithEnv, 28: methodNameWithNoEnv, 29: startupType.FullName)); 30: ? 31: } 32: return null; 33: } 34: if (returnType != null && methodInfo.ReturnType != returnType) 35: { 36: if (required) 37: { 38: throw new InvalidOperationException(string.Format("The '{0}' method in the type '{1}' must have a return type of '{2}'.", 39: methodInfo.Name, 40: startupType.FullName, 41: returnType.Name)); 42: } 43: return null; 44: } 45: return methodInfo; 46: }它查找的第一個委托是ConfigureDelegate,該委托將用于構建應用程序的中間件管道。FindMethod完成了大部分工作,具體的代碼請查看StartupLoader。此方法根據傳遞給它的methodName參數在Startup類中查找響應的方法。我們知道,Startup的定義更多的是約定,所以會去查找Configure和ConfigureServices。當然,通過源代碼我還知道,除了提供標準的“Configure”方法之外,我們還可以通過環境配置找到響應的Configure和ConfigureServices。根本來說,我們最終查找到的是ConfigureContainerDelegate。接下來,一個比較重要的方法是LoadMethods 1: public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, Type startupType, string environmentName) 2: { 3: var configureMethod = FindConfigureDelegate(startupType, environmentName); 4: ? 5: var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName); 6: var configureContainerMethod = FindConfigureContainerDelegate(startupType, environmentName); 7: ? 8: object instance = null; 9: if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic)) 10: { 11: instance = ActivatorUtilities.GetServiceOrCreateInstance(hostingServiceProvider, startupType); 12: } 13: ? 14: // The type of the TContainerBuilder. If there is no ConfigureContainer method we can just use object as it's not 15: // going to be used for anything. 16: var type = configureContainerMethod.MethodInfo != null ? configureContainerMethod.GetContainerType() : typeof(object); 17: ? 18: var builder = (ConfigureServicesDelegateBuilder) Activator.CreateInstance( 19: typeof(ConfigureServicesDelegateBuilder<>).MakeGenericType(type), 20: hostingServiceProvider, 21: servicesMethod, 22: configureContainerMethod, 23: instance); 24: ? 25: return new StartupMethods(instance, configureMethod.Build(instance), builder.Build()); 26: }該方法通過查找對應的方法,由于Startup并未在DI中注冊,所以會調用GetServiceOrCreateInstance創建一個Startup實例,此時構造函數也在此得到解析。通過一系列的調用,最終到達了ConfigureServicesBuilder.Invoke里面。Invoke方法使用反射來獲取和檢查在Startup類上定義的ConfigureServices方法所需的參數。 1: private IServiceProvider InvokeCore(object instance, IServiceCollection services) 2: { 3: if (MethodInfo == null) 4: { 5: return null; 6: } 7: ? 8: // Only support IServiceCollection parameters 9: var parameters = MethodInfo.GetParameters(); 10: if (parameters.Length > 1 || 11: parameters.Any(p => p.ParameterType != typeof(IServiceCollection))) 12: { 13: throw new InvalidOperationException("The ConfigureServices method must either be parameterless or take only one parameter of type IServiceCollection."); 14: } 15: ? 16: var arguments = new object[MethodInfo.GetParameters().Length]; 17: ? 18: if (parameters.Length > 0) 19: { 20: arguments[0] = services; 21: } 22: ? 23: return MethodInfo.Invoke(instance, arguments) as IServiceProvider; 24: }

最后我們來看一下ConfigureBuilder類,它需要一個Action委托變量,其中包含每個IStartupFilter的一組包裝的Configure方法,最后一個是Startup.Configure方法的委托。此時,所調用的配置鏈首先命中的是AutoRequestServicesStartupFilter.Configure方法。并將該委托鏈作為下一個操作,之后會調用ConventionBasedStartup.Configure方法。這將在其本地StartupMethods對象上調用ConfigureDelegate。

1: private void Invoke(object instance, IApplicationBuilder builder) 2: { 3: // Create a scope for Configure, this allows creating scoped dependencies 4: // without the hassle of manually creating a scope. 5: using (var scope = builder.ApplicationServices.CreateScope()) 6: { 7: var serviceProvider = scope.ServiceProvider; 8: var parameterInfos = MethodInfo.GetParameters(); 9: var parameters = new object[parameterInfos.Length]; 10: for (var index = 0; index < parameterInfos.Length; index++) 11: { 12: var parameterInfo = parameterInfos[index]; 13: if (parameterInfo.ParameterType == typeof(IApplicationBuilder)) 14: { 15: parameters[index] = builder; 16: } 17: else 18: { 19: try 20: { 21: parameters[index] = serviceProvider.GetRequiredService(parameterInfo.ParameterType); 22: } 23: catch (Exception ex) 24: { 25: throw new Exception(string.Format( 26: "Could not resolve a service of type '{0}' for the parameter '{1}' of method '{2}' on type '{3}'.", 27: parameterInfo.ParameterType.FullName, 28: parameterInfo.Name, 29: MethodInfo.Name, 30: MethodInfo.DeclaringType.FullName), ex); 31: } 32: } 33: } 34: MethodInfo.Invoke(instance, parameters); 35: } 36: }

Startup.Configure方法會調用ServiceProvider所解析的相應的參數,該方法還可以使用IApplicationBuilder將中間件添加到應用程序管道中。最終的RequestDelegate是從IApplicationBuilder構建并返回的,至此WebHost初始化完成。

原文地址:https://www.cnblogs.com/edison0621/p/10743228.html

.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?

總結

以上是生活随笔為你收集整理的源码里没有configure_深入源码理解.NET Core中Startup的注册及运行的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。