<p>Explore the power of dynamic layout routing in a multi-tenant Blazor Server application. Customize the user experience by assigning unique layouts based on the visitor's domain. Discover the benefits of tailored layouts for each tenant and learn how to implement this functionality in your application. Enhance your multi-tenant application's frontend with this comprehensive overview and code examples.</p>

Creating a Multi-Tenant Blazor Server Application: Part 2 - Frontend Implementation with Dynamic Layout Routing


Introduction:

Welcome to Part 2 of our three-part series on creating a multi-tenant Blazor Server application. In the previous installment, we discussed the advantages of multi-tenancy and compared it with the traditional single-tenant architecture. In this article, we will focus on the frontend implementation of a multi-tenant Blazor Server application, specifically highlighting dynamic layout routing based on the domain.


Routing to Proper Layouts:

To ensure a customized user experience for each tenant, we'll implement dynamic layout routing based on the visitor's domain. By intercepting the routing process, we can dynamically select the appropriate layout for each tenant.


App.razor:

In the `App.razor` file, we configure the routing and inject the necessary services to handle dynamic layout routing. This file serves as the entry point for our multi-tenant Blazor Server application.


@inject DomainLayoutService DomainLayoutService
@inject IHttpContextAccessor HttpContextAccessor
@using BlogCart.Pages
<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        @{
            var domain = HttpContextAccessor.HttpContext.Request.Host.Value.ToLower();
            var layoutType = DomainLayoutService.GetLayoutForDomain(domain);
        }
        <RouteView RouteData="@routeData" DefaultLayout="@layoutType" />
    </Found>
    <NotFound>
        <PageTitle>Not found</PageTitle>
        <LayoutView Layout="@typeof(MainLayout)">
            <_UnderConstruction></_UnderConstruction>
        </LayoutView>
    </NotFound>
</Router>


DomainLayoutService:

The `DomainLayoutService` class contains the logic for mapping domains to their corresponding layouts. It determines the layout based on the domain provided. By leveraging this service, we can assign the appropriate layout to each tenant.


    public class DomainLayoutService
    {
        public Type GetLayoutForDomain(string domain)
        {
            if (domain == "lightningbits.net")
            {
                return typeof(LightningBitsLayout);
            }
            else if (domain == "lightningbits.com")
            {
                return typeof(LightningBitsLayout);
            }
            else if (domain == "bluelemonz.com")
            {
                return typeof(BlueLemonZLayout);
            }
            else if (domain == "stefmancia.com")
            {
                return typeof(StefManciaLayout);
            }
            else if (domain == "thehealerisyou.com")
            {
                return typeof(TheHealerIsYouLayout);
            }
            else if (domain == "gluisi.com")
            {
                return typeof(GLuisiLayout);
            }
           else if (domain == "localhost:7099") //set dev path
            {
                return typeof(LightningBitsLayout);
            }
            else
            {
                return typeof(MainLayout); //fallback route
            }
        }
    }


Client Frontend Service: Click here to View on Github!

The ClientFrontendService is a key component of our multi-tenant Blazor Server application. Its primary responsibility is to retrieve client-specific data based on the accessed domain. By leveraging the HttpClient, it establishes communication with the backend API to fetch the required information for each tenant. This service plays a crucial role in seamlessly integrating the frontend and backend, resulting in personalized and tailored experiences for every client.



Layout.razor and Layout.cshtml:

The Layout.razor file serves as the foundation for rendering tenant-specific content and defines the structure and components shared across multiple pages in your Blazor Server application. It embraces the component-based architecture of Blazor, allowing you to create reusable UI elements that contribute to a consistent user experience. In this implementation, we have chosen to enhance the layout with MudBlazor(View MudBlazor), a preferred UI component library known for its extensive collection of pre-built components, themes, and styling options. The use of MudBlazor is not mandatory, but it offers a convenient way to expedite development and achieve a visually appealing and responsive user interface. The corresponding `Layout.cshtml` file complements the Razor component by providing additional customization options.


<MudLayout Color="Color.Dark">
    <LightningBitsAppBar OnSidebarToggled="ToggleSidebar" />
    <LightningBitsNavMenu SideBarOpen="_sidebarOpen" />
    <MudScrollToTop>
        <MudFab Color="Color.Primary" Icon="@Icons.Filled.ArrowCircleUp" />
    </MudScrollToTop>
    <MudMainContent>
        <CascadingValue Value="ClientId">
            @Body
        </CascadingValue>
    </MudMainContent>
</MudLayout>

@code {
    private int ClientId { get; set; } = 0;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development")
        {
            if (firstRender)
            {
                ClientId = 3; //dev testing client, Also need to set layout in service folder DomianlayoutService to Match
                _isDarkMode = await _mudThemeProvider.GetSystemPreference();
                StateHasChanged();
            }
        }
        else
        {
            if (firstRender)
            {
                var domain = HttpContextAccessor.HttpContext.Request.Host.Value.ToLower();
                ClientId = await ClientFrontendService.GetClientIdFromDomain(domain);
                _isDarkMode = await _mudThemeProvider.GetSystemPreference();
                StateHasChanged();
            }
        }
    }


Index.razor:

The `Index.razor` file represents the root page of our multi-tenant application. It contains a redirect component that ensures users are directed to the appropriate tenant-specific page based on the domain they accessed.


@page "/"
@inject IHttpContextAccessor HttpContextAccessor
@inject NavigationManager _navigationManager
@inject DomainLayoutService _domainLayoutService

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            try
            {
                var domain = HttpContextAccessor.HttpContext.Request.Host.Value.ToLower();
                var layoutType = _domainLayoutService.GetLayoutForDomain(domain);
                var layoutName = layoutType.Name.Replace("Layout", "");
                string landingPageRoute = $"/{layoutName}";
                _navigationManager.NavigateTo(landingPageRoute, true);
            }
            catch (NullReferenceException)
            {
                // This exception may occur during hot reload, so we can safely ignore it.
            }
        }
    }
}


To Be Continued:

In the final part of our series, Part 3, we will explore an essential aspect of building a multi-tenant Blazor Server application: handling the separation of data in the API and database layers. We will discuss techniques for managing tenant-specific data, ensuring data isolation, and optimizing database performance. Don't miss out on this crucial part that completes the trilogy and helps you create a robust and secure multi-tenant application.


Stay tuned for Part 3, where we will dive deep into the implementation details of data separation and management in a multi-tenant Blazor Server application.


Conclusion:

In this second part of our series, we explored the frontend implementation of a multi-tenant Blazor Server application. By intercepting the routing process and dynamically selecting the layout based on the domain, we can provide a customized and consistent user experience for each tenant.


The provided code showcases the main components and services involved in implementing dynamic layout routing and handling tenant-specific data retrieval. Stay tuned for the final part of our series, where we will discuss advanced techniques for managing tenant-specific resources and further optimizing your multi-tenant Blazor Server application.


Don't miss out on the practical examples and insights that will help you create robust and scalable multi-tenant applications.

Written by: Rafael Morel Published on June 02, 2023 Viewed: 702
An error has occurred. This application may no longer respond until reloaded. Reload 🗙