(七)学习了解OrchardCore笔记——多租户路由中间件ModularTenantRouterMiddleware

  先补充下上个中间件缺少介绍的,我发现没那个说不下去,看看上个中间件ModularTenantContainerMiddleware的Invoke方法的第一行

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));
            }
        }

  最终是运行ShellHost的PreCreateAndRegisterShellsAsync方法,再看看

private async Task PreCreateAndRegisterShellsAsync()
        {
            if (_logger.IsEnabled(LogLevel.Information))
            {
                _logger.LogInformation("Start creation of shells");
            }

            // Load all extensions and features so that the controllers are registered in
            // 'ITypeFeatureProvider' and their areas defined in the application conventions.
            var features = _extensionManager.LoadFeaturesAsync();

            // Is there any tenant right now?
            var allSettings = (await _shellSettingsManager.LoadSettingsAsync()).Where(CanCreateShell).ToArray();
            var defaultSettings = allSettings.FirstOrDefault(s => s.Name == ShellHelper.DefaultShellName);
            var otherSettings = allSettings.Except(new[] { defaultSettings }).ToArray();

            await features;

            // The 'Default' tenant is not running, run the Setup.
            if (defaultSettings?.State != TenantState.Running)
            {
                var setupContext = await CreateSetupContextAsync(defaultSettings);
                AddAndRegisterShell(setupContext);
                allSettings = otherSettings;
            }

            if (allSettings.Length > 0)
            {
                // Pre-create and register all tenant shells.
                foreach (var settings in allSettings)
                {
                    AddAndRegisterShell(new ShellContext.PlaceHolder { Settings = settings });
                };
            }

            if (_logger.IsEnabled(LogLevel.Information))
            {
                _logger.LogInformation("Done pre-creating and registering shells");
            }
        }

  看看第二个if里面var setupContext = await CreateSetupContextAsync(defaultSettings);

  这个就算要补充的东西,这个方法会构建每个租户的依赖注入容器去串联每个模块startup,具体可以看看ShellContainerFactory的创建容器方法CreateContainer,这个最好自己追踪下。也就是说既有共有依赖注入,也有每个租户自己依赖注入,这个过程真心值得看,我是无心细说,直接跳到今天的主题。

  直接看ModularTenantRouterMiddleware中间件的Invoke方法

  

public async Task Invoke(HttpContext httpContext)
        {
            if (_logger.IsEnabled(LogLevel.Information))
            {
                _logger.LogInformation("Begin Routing Request");
            }

            var shellContext = ShellScope.Context;

            // Define a PathBase for the current request that is the RequestUrlPrefix.
            // This will allow any view to reference ~/ as the tenant's base url.
            // Because IIS or another middleware might have already set it, we just append the tenant prefix value.
            if (!String.IsNullOrEmpty(shellContext.Settings.RequestUrlPrefix))
            {
                PathString prefix = "/" + shellContext.Settings.RequestUrlPrefix;
                httpContext.Request.PathBase += prefix;
                httpContext.Request.Path.StartsWithSegments(prefix, StringComparison.OrdinalIgnoreCase, out PathString remainingPath);
                httpContext.Request.Path = remainingPath;
            }

            // Do we need to rebuild the pipeline ?
            if (shellContext.Pipeline == null)
            {
                await InitializePipelineAsync(shellContext);
            }

            await shellContext.Pipeline.Invoke(httpContext);
        }

  前面没啥说的,看看最后两行代码,await InitializePipelineAsync(shellContext)和await shellContext.Pipeline.Invoke(httpContext),没有管道就初始化管道,然后调用?what?别理解错了,这个此管道非彼管道,这个按里面的名称来说是租户管道,这也是很多人说OrchardCore的类似asp.net core的原因,接下去看看这个类下面几个方法就明白了,一个套着一个调用,最终是这个

        private void ConfigureTenantPipeline(IApplicationBuilder appBuilder)
        {
            var startups = appBuilder.ApplicationServices.GetServices<IStartup>();

            // IStartup instances are ordered by module dependency with an 'ConfigureOrder' of 0 by default.
            // OrderBy performs a stable sort so order is preserved among equal 'ConfigureOrder' values.
            startups = startups.OrderBy(s => s.ConfigureOrder);

            appBuilder.UseRouting().UseEndpoints(routes =>
            {
                foreach (var startup in startups)
                {
                    startup.Configure(appBuilder, routes, ShellScope.Services);
                }
            });
        }

  appBuilder.UseRouting().UseEndpoints(),知道asp.net core应该都超级熟悉的两个中间件路由中间件和终结点中间件,startups往上看var startups = appBuilder.ApplicationServices.GetServices<IStartup>(),也就是把依赖注入的IStartup实例化,为啥是多个呢,参考前面补充去看下ShellContainerFactory就明白,这是每个模块里的Startup啊。

  本系列到此结束,后面草草收场,有需要讨论留言或者私信。主要第一次写随笔没经验,后面我发现展开难度太大了,光光那个第一行我估计详细解释要几十篇。我看看以后有机会(为啥说有机会呢,因为我不会画图)从OrchardCore的架构说起会清晰点,而且我也没asp.net core的基础详解,很多言语模糊,实在太难展开了。

  

posted @ 2020-07-20 09:59  shuisen  阅读(426)  评论(0编辑  收藏  举报