現在開始看Starpup的中間件。這是一個擴展方法app.UseOrchardCore()
public void Configure(IApplicationBuilder app, IHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseOrchardCore(); }
直接查看擴展方法,vs都是直接按住ctrl鍵+鼠標左鍵就可以跳轉到具體方法,下面我我們定位到方法里面去。
可以清楚的看到下面代碼又是很熟悉的結構。
public static class ApplicationBuilderExtensions { /// <summary> /// Enables multi-tenant requests support for the current path. /// </summary> public static IApplicationBuilder UseOrchardCore(this IApplicationBuilder app, Action<IApplicationBuilder> configure = null) { var env = app.ApplicationServices.GetRequiredService<IHostEnvironment>(); var appContext = app.ApplicationServices.GetRequiredService<IApplicationContext>(); env.ContentRootFileProvider = new CompositeFileProvider( new ModuleEmbeddedFileProvider(appContext), env.ContentRootFileProvider); // Init also the web host 'ContentRootFileProvider'. app.ApplicationServices.GetRequiredService<IWebHostEnvironment>() .ContentRootFileProvider = env.ContentRootFileProvider; app.UseMiddleware<PoweredByMiddleware>(); // Ensure the shell tenants are loaded when a request comes in // and replaces the current service provider for the tenant's one. app.UseMiddleware<ModularTenantContainerMiddleware>(); configure?.Invoke(app); app.UseMiddleware<ModularTenantRouterMiddleware>(app.ServerFeatures); return app; } }
OrchardCore自己也注釋的很清楚:為當前路徑啟用多租戶請求支持。網上很多都介紹OrchardCore是一個多租戶的cms,那么什么是租戶呢?我的理解就是像虛擬主機一類的東西。都知道Program的main方法就是構建一個kestrel主機運行起來(這里我直接當作iis運行起來,這樣比較好理解),kestrel是一個很簡陋的主機,很多功能都沒有,比如像iis那樣的服務器能創建虛擬主機它是沒辦法的,它只能提供一個主機服務,而多租戶就是類似於iis上的虛擬主機了,也就是可以提供多個web站點服務。當然我表述比較土,換個角度,我們要開通web服務,就必須像服務商租用web主機,服務商有一台服務器,而我們只要開一個web服務,因此只要租用一個虛擬主機就夠了而不用租用整台服務器,當然我們朋友要多開個web服務就可以多租一個虛擬主機就夠了。OrchardCore就是web服務器,而我們可以開n個web服務,OrchardCore提供足夠的虛擬主機給我們租用。也就是說每個租戶都是獨立不干擾的虛擬主機,各自提供自己的web服務。我語文比較差,大概就這么個意思。
開始看代碼,前面這幾行沒啥好說的,看過asp.net core源碼的都能直接跳過了!就是獲取環境變量,初始化Web主機“ ContentRootFileProvider”。好像很難理解,其實就是指定租戶的wwwroot文件夾。后面有空再追蹤下這段,先跳過。
var env = app.ApplicationServices.GetRequiredService<IHostEnvironment>(); var appContext = app.ApplicationServices.GetRequiredService<IApplicationContext>(); env.ContentRootFileProvider = new CompositeFileProvider( new ModuleEmbeddedFileProvider(appContext), env.ContentRootFileProvider); // Init also the web host 'ContentRootFileProvider'. app.ApplicationServices.GetRequiredService<IWebHostEnvironment>() .ContentRootFileProvider = env.ContentRootFileProvider;
前面跳過是因為下面這幾行才是整個OrchardCore的靈魂,先搞懂這幾行,其它先旁支忽略吧
app.UseMiddleware<PoweredByMiddleware>(); // Ensure the shell tenants are loaded when a request comes in // and replaces the current service provider for the tenant's one. app.UseMiddleware<ModularTenantContainerMiddleware>(); configure?.Invoke(app); app.UseMiddleware<ModularTenantRouterMiddleware>(app.ServerFeatures);
app.UseMiddleware多么熟悉的結構,就是加了三個中間件PoweredByMiddleware、ModularTenantContainerMiddleware和ModularTenantRouterMiddleware,從名稱上已經可以大致明白他們的作用。分別是版權、模塊化租戶容器和模塊化租戶路由。
PoweredByMiddleware比較簡單,基本點進去直接看源碼就明白了。
/// <summary> /// Adds the X-Powered-By header with values OrchardCore. /// </summary> public class PoweredByMiddleware { private readonly RequestDelegate _next; private readonly IPoweredByMiddlewareOptions _options; public PoweredByMiddleware(RequestDelegate next, IPoweredByMiddlewareOptions options) { _next = next; _options = options; } public Task Invoke(HttpContext httpContext) { if (_options.Enabled) { httpContext.Response.Headers[_options.HeaderName] = _options.HeaderValue; } return _next.Invoke(httpContext); } }
asp.net core中間件熟悉的界面,一個構造函數和一個Invoke(或者異步的InvokeAsync),options也是下面的接口和類直接注入。也就是把Headers[X-Powered-By]的值設置為“OrchardCore”寫入上下文對象。
public interface IPoweredByMiddlewareOptions { bool Enabled { get; set; } string HeaderName { get; } string HeaderValue { get; set; } } internal class PoweredByMiddlewareOptions : IPoweredByMiddlewareOptions { private const string PoweredByHeaderName = "X-Powered-By"; private const string PoweredByHeaderValue = "OrchardCore"; public string HeaderName => PoweredByHeaderName; public string HeaderValue { get; set; } = PoweredByHeaderValue; public bool Enabled { get; set; } = true; }
ModularTenantContainerMiddleware這個中間件就是整個OrchardCore所有能力的體現了。這個Invoke比PoweredByMiddleware的復雜多了,感覺拆開可以講三天三夜,而且坑也很多(是我知識水平太低被坑,而不是說項目不好),下次就篇再分析吧,簡單說就租戶主機沒創建的創建,沒開啟的啟動,收到請求的如何處理請求等等。這個Shell我是翻譯不出意思,直接理解成具體某一個吧,說錯請糾正,這也是我發學習筆記的原因。
/// <summary> /// This middleware replaces the default service provider by the one for the current tenant /// </summary> public class ModularTenantContainerMiddleware { private readonly RequestDelegate _next; private readonly IShellHost _shellHost; private readonly IRunningShellTable _runningShellTable; public ModularTenantContainerMiddleware( RequestDelegate next, IShellHost shellHost, IRunningShellTable runningShellTable) { _next = next; _shellHost = shellHost; _runningShellTable = runningShellTable; } public async Task Invoke(HttpContext httpContext) { // Ensure all ShellContext are loaded and available. await _shellHost.InitializeAsync(); var shellSettings = _runningShellTable.Match(httpContext); // We only serve the next request if the tenant has been resolved. if (shellSettings != null) { if (shellSettings.State == TenantState.Initializing) { httpContext.Response.Headers.Add(HeaderNames.RetryAfter, "10"); httpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; await httpContext.Response.WriteAsync("The requested tenant is currently initializing."); return; } // Makes 'RequestServices' aware of the current 'ShellScope'. httpContext.UseShellScopeServices(); var shellScope = await _shellHost.GetScopeAsync(shellSettings); // Holds the 'ShellContext' for the full request. httpContext.Features.Set(new ShellContextFeature { ShellContext = shellScope.ShellContext, OriginalPath = httpContext.Request.Path, OriginalPathBase = httpContext.Request.PathBase }); await shellScope.UsingAsync(scope => _next.Invoke(httpContext)); } }
最后ModularTenantRouterMiddleware這個中間件一看就跟路由有關,具體代碼自己ctrl+鼠標左鍵點吧,簡單的可以理解成asp.net core web應用程序或者asp.net core web api里的app.UseEndpoints,當然不是這么簡單,畢竟是模塊化,所以有個StartupBase的自定義類要理解下,這也是說OrchardCore的模塊化多租戶不是asp.net core的原因。開始接觸我也想怎么有這么矛盾的事情,明明OrchardCore就是一個asp.net core的程序,怎么有很多觀點說它只是類似於asp.net core而不是asp.net core呢?現在我才明白,沒錯,OrchardCore是asp.net core,但是他提供模塊化多租戶的形式並不是跟asp.net core一樣(不然哪里來的多租戶,廢話)。這個后面有時間再說,下一篇筆記應該是開始讀上面那個ModularTenantContainerMiddleware中間件了。