現在開始看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中介軟體了。