Base solution for your next web application
Open Closed

MAUI Blazor Hybrid App Components - Reusing in ASP.NET Zero MVC Web App - Best Practice? #12493


User avatar
0
JeffMH created

What is the best practice for moving over the MAUI Blazor Hybrid components to an ABP MVC site, please? We have created the MAUI Blazor app, and want to quickly add it to the MVC solution. Hoping others have done this before and you would know best pattern to follow. Thanks in advance!

Markdown is supported
Copy & paste or drag & drop images (max 30 MB per image)

4 Answer(s)
  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @JeffMH

    The best and most sustainable way to reuse Blazor components from your MAUI Blazor Hybrid application in an ASP.NET Zero MVC project is to use a Razor Class Library (RCL) and add Blazor Server hosting capabilities to your existing MVC application.

    Create a Razor Class Library (RCL) for Shared Components

    If you haven't already, the first step is to move all shared components into a separate project.

    1. Add a new Razor Class Library project to your existing solution. For example, name it MyProject.Shared.Components.
    2. Move all reusable .razor components, their corresponding C# code-behind files (.razor.cs), and any component-specific CSS/JS files (located under the wwwroot folder) from your MAUI Blazor project to this new RCL project.
    3. Now, both your MAUI project and your ASP.NET Zero MVC project will add a reference to this RCL project.

    Configure the ASP.NET Zero (MVC) Project to Host Blazor

    Your MVC project needs the Blazor Server infrastructure to be able to render Blazor components. This does not break your existing MVC structure; it simply adds a new capability to it.

    1. Add Required Services: In your Program.cs file (or Startup.cs in older .NET Core versions), add the following line where your services are configured:

      // Next to lines like builder.Services.AddControllersWithViews();
      builder.Services.AddServerSideBlazor();
      
    2. Add the Blazor Hub Endpoint: In the same file, where the application's request pipeline is configured, add MapBlazorHub:

      // After the app.UseRouting(); line
      app.UseEndpoints(endpoints =>
      {
          // Your existing endpoints...
          endpoints.MapBlazorHub(); 
          endpoints.MapControllerRoute(
              name: "default",
              pattern: "{controller=Home}/{action=Index}/{id?}");
          // ... other endpoints
      });
      

      If you are using minimal APIs (with app.Map... syntax), the configuration would look like this:

      // Next to the app.MapControllerRoute(...); line
      app.MapBlazorHub();
      
    3. Add the Required JavaScript File: Open the main layout file in your ASP.NET Zero project (_Layout.cshtml) and add the Blazor JavaScript file just before the closing </body> tag.

      <script src="_framework/blazor.server.js"></script>
      </body>
      </html>
      

    Use Blazor Components in MVC Views

    Your MVC project is now ready to render Blazor components. To do this, you will use the Component Tag Helper.

    1. Import Namespaces: To make it easier to use your components, add the namespaces for your RCL project and the Tag Helpers to the _ViewImports.cshtml file.

      @using MyProject.Shared.Components // Your RCL project's namespace
      @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
      @addTagHelper *, MyProject.Web.Mvc // Your MVC project's name (if applicable)
      
    2. Render the Component: Now, inside any .cshtml view file, you can call your Blazor component like this:

      <h2>This is an MVC View</h2>
      <p>Below is a component rendered from Blazor:</p>
      
      <div id="my-blazor-component">
          <component type="typeof(MyAwesomeComponent)" render-mode="ServerPrerendered" />
      </div>
      
      @* If you want to pass parameters to your component: *@
      <component type="typeof(UserProfileCard)" render-mode="ServerPrerendered" param-UserId="@Model.UserId" />
      
    • type: The full type of the component you want to use.
    • render-mode:
      • ServerPrerendered: When the page first loads, the component's HTML is generated on the server and sent in the initial response. After the page loads in the browser, Blazor establishes a SignalR connection to make the component interactive. This is usually the best option for SEO and initial load performance.
      • Server: Only a placeholder is rendered when the page loads. The component's content is loaded after the SignalR connection is established.

    • Styling (CSS): ASP.NET Zero comes with the Metronic theme. To ensure your Blazor components look consistent with this theme, make sure to use Metronic's CSS classes directly within your components. If you add your own styles to the wwwroot folder in the RCL, you must ensure these styles are also included in the MVC project.
    • Authentication and Authorization: Since Blazor Server runs on the existing HttpContext, the user's identity flows automatically to the components. You can use the [Authorize] attribute and the AuthenticationStateProvider in your components just as you would in the MVC project.
    • JavaScript Interaction (JS Interop): If your Blazor components need to interact with jQuery or other JavaScript libraries used by ASP.NET Zero, you will need to do this through the IJSRuntime service.
    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    JeffMH created

    Hi @JeffMH

    The best and most sustainable way to reuse Blazor components from your MAUI Blazor Hybrid application in an ASP.NET Zero MVC project is to use a Razor Class Library (RCL) and add Blazor Server hosting capabilities to your existing MVC application.

    Create a Razor Class Library (RCL) for Shared Components

    If you haven't already, the first step is to move all shared components into a separate project.

    1. Add a new Razor Class Library project to your existing solution. For example, name it MyProject.Shared.Components.
    2. Move all reusable .razor components, their corresponding C# code-behind files (.razor.cs), and any component-specific CSS/JS files (located under the wwwroot folder) from your MAUI Blazor project to this new RCL project.
    3. Now, both your MAUI project and your ASP.NET Zero MVC project will add a reference to this RCL project.

    Configure the ASP.NET Zero (MVC) Project to Host Blazor

    Your MVC project needs the Blazor Server infrastructure to be able to render Blazor components. This does not break your existing MVC structure; it simply adds a new capability to it.

    1. Add Required Services: In your Program.cs file (or Startup.cs in older .NET Core versions), add the following line where your services are configured:

      // Next to lines like builder.Services.AddControllersWithViews(); 
      builder.Services.AddServerSideBlazor(); 
      
    2. Add the Blazor Hub Endpoint: In the same file, where the application's request pipeline is configured, add MapBlazorHub:

      // After the app.UseRouting(); line 
      app.UseEndpoints(endpoints => 
      { 
          // Your existing endpoints... 
          endpoints.MapBlazorHub();  
          endpoints.MapControllerRoute( 
              name: "default", 
              pattern: "{controller=Home}/{action=Index}/{id?}"); 
          // ... other endpoints 
      }); 
      

      If you are using minimal APIs (with app.Map... syntax), the configuration would look like this:

      // Next to the app.MapControllerRoute(...); line 
      app.MapBlazorHub(); 
      
    3. Add the Required JavaScript File: Open the main layout file in your ASP.NET Zero project (_Layout.cshtml) and add the Blazor JavaScript file just before the closing </body> tag.

      <script src="_framework/blazor.server.js"></script> 
      </body> 
      </html> 
      

    Use Blazor Components in MVC Views

    Your MVC project is now ready to render Blazor components. To do this, you will use the Component Tag Helper.

    1. Import Namespaces: To make it easier to use your components, add the namespaces for your RCL project and the Tag Helpers to the _ViewImports.cshtml file.

      @using MyProject.Shared.Components // Your RCL project's namespace 
      @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 
      @addTagHelper *, MyProject.Web.Mvc // Your MVC project's name (if applicable) 
      
    2. Render the Component: Now, inside any .cshtml view file, you can call your Blazor component like this:

      <h2>This is an MVC View</h2> 
      <p>Below is a component rendered from Blazor:</p> 
      
      <div id="my-blazor-component"> 
          <component type="typeof(MyAwesomeComponent)" render-mode="ServerPrerendered" /> 
      </div> 
      
      @* If you want to pass parameters to your component: *@ 
      <component type="typeof(UserProfileCard)" render-mode="ServerPrerendered" param-UserId="@Model.UserId" /> 
      
    • type: The full type of the component you want to use.
    • render-mode:
      • ServerPrerendered: When the page first loads, the component's HTML is generated on the server and sent in the initial response. After the page loads in the browser, Blazor establishes a SignalR connection to make the component interactive. This is usually the best option for SEO and initial load performance.
      • Server: Only a placeholder is rendered when the page loads. The component's content is loaded after the SignalR connection is established.

    • Styling (CSS): ASP.NET Zero comes with the Metronic theme. To ensure your Blazor components look consistent with this theme, make sure to use Metronic's CSS classes directly within your components. If you add your own styles to the wwwroot folder in the RCL, you must ensure these styles are also included in the MVC project.
    • Authentication and Authorization: Since Blazor Server runs on the existing HttpContext, the user's identity flows automatically to the components. You can use the [Authorize] attribute and the AuthenticationStateProvider in your components just as you would in the MVC project.
    • JavaScript Interaction (JS Interop): If your Blazor components need to interact with jQuery or other JavaScript libraries used by ASP.NET Zero, you will need to do this through the IJSRuntime service.

    Thank you for the quick response!

    Are there considerations we need to make for the WebRequestExecutor, please? Since we were calling the APIs that way before - do we continue using that same pattern from the MVC site?

    Thanks!

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    JeffMH created

    Also, could we add a /BlazorApp route and use a MainLayout like the typical Blazor approach and host all our components with Blazor handling everything there? If so, can you provide an example? That may make things much easier, rather than having to use the Blazor as components in CSHTML views with MVC routes. Looking forward to your thoughts. Thanks!

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @JeffMH

    No, you should not use WebRequestExecutor to call your APIs from Blazor components running on the ASP.NET Zero MVC site. It is a pattern designed specifically for client side applications.

    • The Purpose of WebRequestExecutor: This service is designed for client applications (like MAUI, Angular) to make secure HTTP requests to your backend API. It simplifies development by automatically attaching necessary headers like Authorization (the JWT token) and __tenant for authentication and tenancy.
    • The Situation in MVC: Your MVC project is the server. Your Blazor Server components live and execute within the same server process. Making an HTTP call from the server back to itself (like a request to localhost) is an unnecessary network hop. This degrades performance, adds latency, and introduces a needless layer of complexity and a potential point of failure.

    This approach is significantly faster, cleaner, and less error prone. The existing authentication and tenant context from the HttpContext is automatically flowed to the Application Service, so you don't need to manage it manually.


    A Full Blazor Experience with a /BlazorApp Route

    Yes, this is absolutely possible and is an excellent approach, especially if you plan to migrate a significant portion of your application to Blazor. You can create a self contained "island" within your MVC project where Blazor controls its own routing and layout.

    This often provides a cleaner migration path than embedding components one by one into various .cshtml views.

    Create a 'Host' Controller and View in the MVC Project

    This controller will catch all requests that start with /BlazorApp and will serve a single view whose only purpose is to host the Blazor application.

    • BlazorAppController.cs (Add this to your MVC project's Controllers folder)

      using Microsoft.AspNetCore.Mvc;
      
      public class BlazorAppController : Controller
      {
          // This route catches /BlazorApp and any path underneath it
          // e.g., /BlazorApp/users, /BlazorApp/products/details/5
          [Route("BlazorApp/{*pathInfo}")]
          public IActionResult Index()
          {
              return View();
          }
      }
      
    • Views/BlazorApp/Index.cshtml (Create this new view) This view will be very minimal. It will not use the standard MVC _Layout.cshtml and will only contain what's necessary to bootstrap Blazor.

      @{
          // Disable the MVC layout, as Blazor will manage its own.
          Layout = null;
      }
      
      <!DOCTYPE html>
      <html>
      <head>
          <title>Blazor Application</title>
          <base href="/BlazorApp/" /> @* This is very important! It tells Blazor what the base path is. *@
          <link href="/path/to/your/main/styles.css" rel="stylesheet" />
      </head>
      <body>
          @* Render the root component of the Blazor application *@
          <component type="typeof(YourNamespace.Shared.Components.App)" render-mode="ServerPrerendered" />
      
          <script src="_framework/blazor.server.js"></script>
      </body>
      </html>
      

      Note: Replace YourNamespace.Shared.Components.App with the actual namespace of your App.razor component.

    Create App.razor and MainLayout.razor in Your Shared Library (RCL)

    • App.razor (Add this to the root of your RCL project) This component contains the Blazor Router, which decides which page to display based on the incoming URL.

      <Router AppAssembly="@typeof(App).Assembly">
          <Found Context="routeData">
              <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
              <FocusOnNavigate RouteData="@routeData" Selector="h1" />
          </Found>
          <NotFound>
              <LayoutView Layout="@typeof(MainLayout)">
                  <p role="alert">Sorry, there's nothing at this address.</p>
              </LayoutView>
          </NotFound>
      </Router>
      
    • MainLayout.razor (Add this to a Shared folder or similar in your RCL) This is the master template for all pages within your Blazor "island." You can put common elements like a navigation menu, header, and footer here.

      @inherits LayoutComponentBase
      
      <div class="page">
          <div class="sidebar">
              @* You can add your Blazor-specific navigation menu (NavMenu) here *@
              <NavMenu />
          </div>
      
          <main>
              <div class="top-row px-4">
                  <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
              </div>
      
              <article class="content px-4">
                  @Body @* This is where the content of the current page will be rendered *@
              </article>
          </main>
      </div>
      

    Create Your Blazor Pages

    Now you can create pages in your RCL using the @page directive.

    • Example Page: Pages/Dashboard.razor
      @page "/" @* This will be the default page for /BlazorApp/ *@
      @page "/dashboard" @* This will be accessible at /BlazorApp/dashboard *@
      
      <h3>Blazor Dashboard</h3>
      
      <p>Welcome to the Blazor side of the application!</p>
      

    How It Works

    1. A user navigates to https://yoursite.com/BlazorApp/dashboard.
    2. The ASP.NET Core routing engine matches the [Route("BlazorApp/{*pathInfo}")] attribute and invokes the BlazorAppController.
    3. The controller returns the Index.cshtml view.
    4. That view renders the App.razor component via the <component type="typeof(App)"... tag helper.
    5. The <Router> inside App.razor inspects the rest of the URL path (/dashboard), finds the component with a matching @page directive (Dashboard.razor), and renders it inside the MainLayout.razor where @Body is located.

    With this pattern, you cleanly separate all your Blazor logic from MVC and create a coherent, self contained part of your application.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)