Abp Vnext Blazor replace UI component integration bootstrap blazor (detailed process)

The blazor project of Abp Vnext uses Blazorise , but after trial, it is found that multiple tags are not supported. So I want to replace it with BootstrapBlazor.
The process is complicated. I have written the module and just need to replace it.

Click to view the source code

demo is also in the source code

Create an Abp module

Download from the official website

Q: Why not choose an application?

Because the module contains the ssr of Blazor and the host of wasm. It can be used directly. When creating an application, you can only choose one from the host of ssr or wasm. Although you can create it twice and then copy and merge the host, it's too troublesome.

Reduced module

Delete the following useless directories:

  • Angular (front end)
  • Host / demoapp.web.host (MVC use)
  • Host / demoapp.web.unified (MVC use)
  • Host / demoapp.web (used by MVC)

Project structure and how to start the project

  • IdentityServer application is an authentication server used by other applications. It has its own appsettings.json, including database connection string and other configurations. It needs to initialize the database
  • HTTP API of HttpApi.Host managed module. It has its own appsettings.json, including database connection string and other configurations
    Start the project first
  • The startup program of razor.hostrazor webassembly mode has its own appsettings.json (located in wwwroot), including HTTP API server address, IdentityServer and other configurations. The front and back ends are separated. The first two programs need to be started before they can be used normally
  • The startup program of razor.server.hostrazor server mode has its own appsettings.json, including database connection string and other configurations, but it integrates IdentityServer and HttpApi.Host module by default, which is equivalent to that the front and rear ends are not separated, so it can be used directly.

Start project (WebAssembly mode)

Because the default database of the project is MSSQLLocalDB, there is no need to modify the configuration. You can initialize the database directly.

First, switch to the directory where the DemoApp.IdentityServer project is located in the console and execute

dotnet ef database update

Open the following items in order:

  • DemoApp.IdentityServer
  • DemoApp.HttpApi.Host
  • DemoApp.Blazor.Host

open https://localhost:44307/ Load the wasm page normally, and click the login in the upper right corner to jump to the identityServer authentication center( https://localhost:44364/ ), enter the user name admin password 1q2w3E * after login, jump back to wasm

Start project (Server mode)

Because Server.Host integrates IdentityServer and HttpApi by default (it needs to be modified, which will be described later)
Initialize database
First, switch to the directory where the DemoApp.Blazor.Server.Host project is located in the console and execute

dotnet ef database update

Open after direct start https://localhost:44313/ that will do

You can see that it's the same when you log in https://localhost:44313/ , unlike wasm, it will jump to the identity server (because it is integrated by itself).

Replace module theme

DemoApp.Blazor

This is the Blazor public project of the module. Generally, relevant pages and components are written here

  1. Remove the dependency Volo.Abp.AspNetCore.Components.Web.Theming and replace with abp.aspnetcore.razor.theme.bootstrap.
  2. Open DemoAppBlazorModule
    2.1 change the dependent module name abpaspnetcomponentswebthemingmodule in DependsOn to abpaspnetcoreblazerthemebootstrapmodule
    2.2 reference the Abp.AspNetCore.Blazor.Theme.Bootstrap Abp.AspNetCore.Blazor.Theme namespace
  3. Open_ Imports.razor, delete @ using Volo.Abp.BlazoriseUI @using Blazorise @using Blazorise.DataGrid, and add @ using BootstrapBlazor.Components @using Abp.AspNetCore.Blazor.Theme

DemoApp.Blazor.Server

This is the class library referenced by the module in the ssr mode. This is simple. You only need to replace the dependency.

  1. Remove the dependency volo.abp.aspnetcore.components.server.theme and replace it with abp.aspnetcore.razor.theme.bootstrap.server
  2. Open the DemoAppBlazorServerModule
    2.1 change the dependent module name abpaspnetcomponentsserverthemgmodule in DependsOn to abpaspnetcoreblazor themebootstrapservermodule
    2.2 reference the abp.aspnetcore.razor.theme.bootstrap namespace

DemoApp.Blazor.WebAssembly

This is the class library referenced in the wasm mode of the module, which is provided by the.

  1. Remove the dependency volo.abp.aspnetcore.components.webassembly.theme and replace it with abp.aspnetcore.razor.theme.bootstrap.webassembly
  2. Open DemoAppBlazorWebAssemblyModule
    2.1 change the dependent module name in DependsOn from abpaspnetcorecomponentswebassembly themingmodule to AbpAspNetCoreBlazorThemeBootstrapWebAssemblyModule
    2.2 reference the abp.aspnetcore.razor.theme.bootstrap namespace

Replace Host theme

Blazor.Host

First, we replace the topic of WebAssembly Host, which is a little simpler than Server integration

Remove dependency

Since the built-in UI modules such as user management, permission management and tenant management all rely on Blazorise, these items need to be removed from the project dependency:

  • Volo.Abp.Identity.Blazor.WebAssembly
  • Volo.Abp.TenantManagement.Blazor.WebAssembly
  • Volo.Abp.SettingManagement.Blazor.WebAssembly
  • Volo.abp.aspnetcore.components.webassembly.basictheme (topic)
  • Blazorise.Bootstrap
  • Blazorise.Icons.FontAwesome

Modify DemoAppBlazorHostModule

using System;
using System.Net.Http;
using Abp.AspNetCore.Blazor.Theme;
using Abp.AspNetCore.Blazor.Theme.Bootstrap;
using DemoApp.Blazor.WebAssembly;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Account;
using Volo.Abp.Autofac.WebAssembly;
using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
using Volo.Abp.UI.Navigation;
namespace DemoApp.Blazor.Host
{
    [DependsOn(
        typeof(AbpAutofacWebAssemblyModule),
        typeof(AbpAccountApplicationContractsModule), 
        typeof(DemoAppBlazorWebAssemblyModule)
    )]
    public class DemoAppBlazorHostModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var environment = context.Services.GetSingletonInstance<IWebAssemblyHostEnvironment>();
            var builder = context.Services.GetSingletonInstance<WebAssemblyHostBuilder>();
            ConfigureAuthentication(builder);
            ConfigureHttpClient(context, environment);
            ConfigureRouter(context);
            ConfigureUI(builder);
            ConfigureMenu(context);
            ConfigureAutoMapper(context);
        }

        private void ConfigureRouter(ServiceConfigurationContext context)
        {
            Configure<AbpRouterOptions>(options =>
            {
                //options.AppAssembly = typeof(DemoAppBlazorHostModule).Assembly; Note it out here
                options.AdditionalAssemblies.Add(this.GetType().Assembly);
            });
        }

        private void ConfigureMenu(ServiceConfigurationContext context)
        {
            Configure<AbpNavigationOptions>(options =>
            {
                options.MenuContributors.Add(new DemoAppHostMenuContributor(context.Services.GetConfiguration()));
            });
        }

        
        private static void ConfigureAuthentication(WebAssemblyHostBuilder builder)
        {
            builder.Services.AddOidcAuthentication(options =>
            {
                builder.Configuration.Bind("AuthServer", options.ProviderOptions);
                options.ProviderOptions.DefaultScopes.Add("DemoApp");
            });
        }

        private static void ConfigureUI(WebAssemblyHostBuilder builder)
        {
            builder.RootComponents.Add<App>("#ApplicationContainer");
        }

        private static void ConfigureHttpClient(ServiceConfigurationContext context, IWebAssemblyHostEnvironment environment)
        {
            context.Services.AddTransient(sp => new HttpClient
            {
                BaseAddress = new Uri(environment.BaseAddress)
            });
        }

        private void ConfigureAutoMapper(ServiceConfigurationContext context)
        {
            Configure<AbpAutoMapperOptions>(options =>
            {
                options.AddMaps<DemoAppBlazorHostModule>();
            });
        }
    }
}

Modification_ Imports.razor

delete

@using Blazorise
@using Blazorise.DataGrid

add to

@using BootstrapBlazor.Components
@using Abp.AspNetCore.Blazor.Theme

Regenerate style

The bundle needs to be re created because the theme has been modified

Mr. Zhang becomes the DemoApp.Blazor.Host project, and then transfers to the directory of DemoApp.Blazor.Host on the console
Execution:

abp bundle

If abp is not displayed, you need to install it abp-cli

Display after login:

Blazor.Server.Host

1. Remove and replace dependencies

Remove the following packages

  • Blazorise.Bootstrap
  • Blazorise.Icons.FontAwesome
  • Microsoft.EntityFrameworkCore.Tools
  • Volo.Abp.EntityFrameworkCore.SqlServer
  • Volo.Abp.AspNetCore.Authentication.JwtBearer
  • Volo.Abp.AspNetCore.Components.Server.BasicTheme
  • Volo.Abp.AuditLogging.EntityFrameworkCore
  • Volo.Abp.Account.Web.IdentityServer
  • Volo.Abp.Account.Application
  • Volo.Abp.FeatureManagement.EntityFrameworkCore
  • Volo.Abp.FeatureManagement.Application
  • Volo.Abp.Identity.Blazor.Server
  • Volo.Abp.Identity.EntityFrameworkCore
  • Volo.Abp.Identity.Application
  • Volo.Abp.TenantManagement.Blazor.Server
  • Volo.Abp.TenantManagement.EntityFrameworkCore
  • Volo.Abp.TenantManagement.Application
  • Volo.Abp.SettingManagement.Blazor.Server
  • Volo.Abp.SettingManagement.EntityFrameworkCore
  • Volo.Abp.SettingManagement.Application
  • Volo.Abp.PermissionManagement.Application
  • Volo.Abp.PermissionManagement.EntityFrameworkCore
  • DemoApp.EntityFrameworkCore\DemoApp.EntityFrameworkCore
  • DemoApp.HttpApi

Add the following packages

  • Volo.Abp.AspNetCore.Authentication.OpenIdConnect
  • Volo.Abp.AspNetCore.Mvc.Client
  • Volo.Abp.AspNetCore.Authentication.OAuth
  • Volo.Abp.Http.Client.IdentityModel.Web
  • Volo.Abp.PermissionManagement.HttpApi.Client
  • Volo.Abp.Identity.HttpApi.Client
  • Volo.Abp.TenantManagement.HttpApi.Client
  • Volo.Abp.FeatureManagement.HttpApi.Client
  • DemoApp.HttpApi.Client

2. Modify Module.cs

1. Delete the removed module in DependsOn

Also delete

  • DemoAppEntityFrameworkCoreModule (because you don't need to read the database directly)

  • DemoAppApplicationModule

  • DemoAppHttpApiModule
    Add the following modules

  • AbpAspNetCoreMvcClientModule

  • AbpAspNetCoreAuthenticationOAuthModule

  • AbpAspNetCoreAuthenticationOpenIdConnectModule

  • AbpHttpClientIdentityModelWebModule

  • AbpAspNetCoreMvcUiBasicThemeModule

  • AbpAspNetCoreSerilogModule

  • AbpIdentityHttpApiClientModule

  • AbpFeatureManagementHttpApiClientModule

  • AbpTenantManagementHttpApiClientModule

  • AbpPermissionManagementHttpApiClientModule

2.ConfigureServices
   public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var hostingEnvironment = context.Services.GetHostingEnvironment();
            var configuration = context.Services.GetConfiguration();
            Configure<AbpBundlingOptions>(options =>
            {
                // MVC UI
                options.StyleBundles.Configure(
                    BasicThemeBundles.Styles.Global,
                    bundle =>
                    {
                        bundle.AddFiles("/global-styles.css");
                    }
                );

                //BLAZOR UI
                options.StyleBundles.Configure(
                    BlazorBootstrapThemeBundles.Styles.Global,
                    bundle =>
                    {
                        bundle.AddFiles("/blazor-global-styles.css");
                        //You can remove the following line if you don't use Blazor CSS isolation for components
                        bundle.AddFiles("/DemoApp.Blazor.Server.Host.styles.css");
                    }
                );
            });
            
            context.Services.AddAuthentication(options =>
                {
                    options.DefaultScheme = "Cookies";
                    options.DefaultChallengeScheme = "oidc";
                })
                .AddCookie("Cookies", options => { options.ExpireTimeSpan = TimeSpan.FromDays(365); })
                .AddAbpOpenIdConnect("oidc", options =>
                {
                    options.Authority = configuration["AuthServer:Authority"];
                    options.ClientId = configuration["AuthServer:ClientId"];
                    options.ClientSecret = configuration["AuthServer:ClientSecret"];
                    options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
                    options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
                    options.SaveTokens = true;
                    options.GetClaimsFromUserInfoEndpoint = true;
                    options.Scope.Add("role");
                    options.Scope.Add("email");
                    options.Scope.Add("phone");
                    options.Scope.Add("DemoApp");
                });
            if(hostingEnvironment.IsDevelopment())
            {
                Configure<AbpVirtualFileSystemOptions>(options =>
                {
                    options.FileSets.ReplaceEmbeddedByPhysical<DemoAppDomainSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}DemoApp.Domain.Shared", Path.DirectorySeparatorChar)));
                    options.FileSets.ReplaceEmbeddedByPhysical<DemoAppDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}DemoApp.Domain", Path.DirectorySeparatorChar)));
                    options.FileSets.ReplaceEmbeddedByPhysical<DemoAppApplicationContractsModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}DemoApp.Application.Contracts", Path.DirectorySeparatorChar)));
                    options.FileSets.ReplaceEmbeddedByPhysical<DemoAppApplicationModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}DemoApp.Application", Path.DirectorySeparatorChar)));
                    options.FileSets.ReplaceEmbeddedByPhysical<DemoAppBlazorHostModule>(hostingEnvironment.ContentRootPath);
                });
            }

            context.Services.AddAbpSwaggerGen(
                options =>
                {
                    options.SwaggerDoc("v1", new OpenApiInfo { Title = "DemoApp API", Version = "v1" });
                    options.DocInclusionPredicate((docName, description) => true);
                    options.CustomSchemaIds(type => type.FullName);
                });

            Configure<AbpLocalizationOptions>(options =>
            {
                options.Languages.Add(new LanguageInfo("cs", "cs", "Čeština"));
                options.Languages.Add(new LanguageInfo("en", "en", "English"));
                options.Languages.Add(new LanguageInfo("en-GB", "en-GB", "English (UK)"));
                options.Languages.Add(new LanguageInfo("fi", "fi", "Finnish"));
                options.Languages.Add(new LanguageInfo("fr", "fr", "Français"));
                options.Languages.Add(new LanguageInfo("hi", "hi", "Hindi", "in"));
                options.Languages.Add(new LanguageInfo("it", "it", "Italian", "it"));
                options.Languages.Add(new LanguageInfo("hu", "hu", "Magyar"));
                options.Languages.Add(new LanguageInfo("pt-BR", "pt-BR", "Português (Brasil)"));
                options.Languages.Add(new LanguageInfo("ru", "ru", "Русский"));
                options.Languages.Add(new LanguageInfo("sk", "sk", "Slovak"));
                options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe"));
                options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "Simplified Chinese"));
                options.Languages.Add(new LanguageInfo("zh-Hant", "zh-Hant", "Traditional Chinese"));
            });

            Configure<AbpMultiTenancyOptions>(options =>
            {
                options.IsEnabled = MultiTenancyConsts.IsEnabled;
            });

            context.Services.AddTransient(sp => new HttpClient
            {
                BaseAddress = new Uri("/")
            });

          

            Configure<AbpNavigationOptions>(options =>
            {
                options.MenuContributors.Add(new DemoAppMenuContributor());
            });

// Configure<AbpRouterOptions>(options => { options.AppAssembly = typeof(DemoAppBlazorHostModule).Assembly; });
            Configure<AbpRouterOptions>(options => { options.AdditionalAssemblies .Add(typeof(DemoAppBlazorHostModule).Assembly); });//Change it to this
        }
3.OnApplicationInitialization
 public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var env = context.GetEnvironment();
            var app = context.GetApplicationBuilder();

            app.UseAbpRequestLocalization();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseCorrelationId();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthentication();
            //app.UseJwtTokenMiddleware();

            if (MultiTenancyConsts.IsEnabled)
            {
                app.UseMultiTenancy();
            }

            // app.UseUnitOfWork();
            //app.UseIdentityServer();
            app.UseAuthorization();
            app.UseSwagger();
            app.UseAbpSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "DemoApp API"); });
            app.UseConfiguredEndpoints();

            using (var scope = context.ServiceProvider.CreateScope())
            {
                AsyncHelper.RunSync(async () =>
                {
                    await scope.ServiceProvider
                        .GetRequiredService<IDataSeeder>()
                        .SeedAsync();
                });
            }
        }

3. Modification_ Imports.razor

delete

@using Blazorise
@using Blazorise.DataGrid
@using Volo.Abp.BlazoriseUI
@using Volo.Abp.BlazoriseUI.Components

add to

@using BootstrapBlazor.Components
@using Abp.AspNetCore.Blazor.Theme

4. Delete the EntityFrameworkCore and Migrations directories

Because we directly call httpApi to obtain data, we do not need host to read the database, so we delete these two directories

5._Host.cshtml

@page "/"
@namespace DemoApp.Blazor.Server.Host.Pages
@using System.Globalization
@using Abp.AspNetCore.Blazor.Theme.Bootstrap
@using Abp.AspNetCore.Blazor.Theme.Server
@using Volo.Abp.Localization
@{
    Layout = null;
    var rtl = CultureHelper.IsRtl ? "rtl" : string.Empty;
}

<!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name" dir="@rtl">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>DemoApp.Blazor.Server</title>
    <base href="~/" />

    <abp-style-bundle name="@BlazorBootstrapThemeBundles.Styles.Global" />
</head>
<body class="abp-application-layout bg-light @rtl">
    <component type="typeof(App)" render-mode="Server" />

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <abp-script-bundle name="@BlazorBootstrapThemeBundles.Scripts.Global" />
</body>
</html>

6.DemoAppMenuContributor

Comment on the ConfigureMainMenuAsync method body, because we don't have those modules

7. Modify appsettings.json configuration

Delete ConnectionStrings node

Modify AuthServer to:

 "AuthServer": {
    "Authority": "https://localhost:44364",
    "RequireHttpsMetadata": "true",
    "ClientId": "DemoApp_Blazor_Server",
    "ClientSecret": "1q2w3e*"
  }

The Authority configuration item is the URI of IdentityServer, and the ClientID needs to be remembered, which will be used later

add to:

  "RemoteServices": {
    "Default": {
      "BaseUrl": "https://localhost:44396/"
    }
  }

The uri of the HTTP API is configured here

5. Add login controller

Create the Controllers directory and add the AccountController

    public class AccountController : ChallengeAccountController
    {

    }

6. Add identityServer configuration

Open the DemoApp.IdentityServer project

1. Modify appsettings.json

Add in Clients of identity server

      "DemoApp_Blazor_Server": {
        "ClientId": "DemoApp_Blazor_Server",
        "RootUrl": "https://localhost:44313/"
        "ClientSecret": "1q2w3e*",
      }

Navigate to IdentityServer/IdentityServerDataSeedContributor.cs and add the IdentityServer configuration.

Modify the CreateClientsAsync method and add

      var blazorServerTieredClientId = configurationSection["DemoApp_Blazor_Server:ClientId"];
            if (!blazorServerTieredClientId.IsNullOrWhiteSpace())
            {
                var blazorServerTieredClientRootUrl = configurationSection["DemoApp_Blazor_Server:RootUrl"].EnsureEndsWith('/');

                /* Admin_BlazorServerTiered client is only needed if you created a tiered blazor server
                 * solution. Otherwise, you can delete this client. */

                await CreateClientAsync(
                    name: blazorServerTieredClientId,
                    scopes: commonScopes,
                    grantTypes: new[] { "hybrid" },
                    secret: (configurationSection["DemoApp_Blazor_Server:ClientSecret"] ?? "1q2w3e*").Sha256(),
                    redirectUri: $"{blazorServerTieredClientRootUrl}signin-oidc",
                    postLogoutRedirectUri: $"{blazorServerTieredClientRootUrl}signout-callback-oidc",
                    frontChannelLogoutUri: $"{blazorServerTieredClientRootUrl}Account/FrontChannelLogout",
                    corsOrigins: new[] { blazorServerTieredClientRootUrl.RemovePostFix("/") }
                );
            }

After modification, you need to reopen the identity server configuration to take effect.

7. Modify menu

Navigate to menus > demoappmenucontributor.cs

using System.Threading.Tasks;
using DemoApp.MultiTenancy;
using Volo.Abp.UI.Navigation;

namespace DemoApp.Blazor.Server.Host.Menus
{
    public class DemoAppMenuContributor : IMenuContributor
    {
        public async Task ConfigureMenuAsync(MenuConfigurationContext context)
        {
            if (context.Menu.Name == StandardMenus.Main)
            {
                await ConfigureMainMenuAsync(context);
            }
            
            
              
        }

        private Task ConfigureMainMenuAsync(MenuConfigurationContext context)
        {
            var administration = context.Menu.GetAdministration();
            context.Menu.Items.Insert(0,
                new ApplicationMenuItem("Index", displayName: "Index", "/", icon: "fa fa-home"));
            // if (MultiTenancyConsts.IsEnabled)
            // {
            //     administration.SetSubItemOrder(TenantManagementMenuNames.GroupName, 1);
            // }
            // else
            // {
            //     administration.TryRemoveMenuItem(TenantManagementMenuNames.GroupName);
            // }
            //
            // administration.SetSubItemOrder(IdentityMenuNames.GroupName, 2);
            // administration.SetSubItemOrder(SettingManagementMenus.GroupName, 3);

            return Task.CompletedTask;
        }
    }
}

Incomplete

Because several page modules in abp have been removed, you need to rewrite user management, role management, tenant management and other pages. I will release these modules after I improve them. Also, the login page of identity server should be rewritten.

Keywords: C# .NET Blazor

Added by rookie on Mon, 29 Nov 2021 12:21:14 +0200