(二)學習瞭解OrchardCore筆記——開篇:OrchardCore的中介軟體

shuisen發表於2020-07-10

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

相關文章