• (七)学习了解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的基础详解,很多言语模糊,实在太难展开了。

      

  • 相关阅读:
    Windows内核对象
    FreeWriting_1
    FreeWriting_2
    【整理】技术文章集锦
    【转】英语吵架一百句
    像 IDE 一样使用 vim
    统治世界的十大算法
    AnimationSet动画集合类的使用
    帮你解答adb是什么,adb有什么用
    SharedPreferences的简单使用
  • 原文地址:https://www.cnblogs.com/ShuiSen/p/13343527.html
Copyright © 2020-2023  润新知