Check the docs before asking a question: https://docs.abp.io/en/commercial/latest/
Check the samples, to see the basic tasks: https://docs.abp.io/en/commercial/latest/samples/index
The exact solution to your question may have been answered before, please use the search on the homepage.
If you're creating a bug/problem report, please include followings:
-
ABP Framework version: v5.3.1
-
UI type: Blazor
-
DB provider: EF Core
-
Tiered (MVC) or Identity Server Separated (Angular): no
-
Exception message and stack trace:
-
Steps to reproduce the issue:"
When I run my application in dev and try to browse to the Hangfire dashboard I get a 404 page not found:
Here is how I have Hangfire configured in my Blazor project's module:
using System;
using System.IO;
using System.Threading.Tasks;
using Blazorise.Bootstrap5;
using Blazorise.Icons.FontAwesome;
using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
using Microsoft.AspNetCore.Authentication.Twitter;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Pol.Blazor.Menus;
using Pol.EntityFrameworkCore;
using Pol.Localization;
using Pol.MultiTenancy;
using Microsoft.OpenApi.Models;
using Pol.Blazor.Components.Layout;
using Volo.Abp;
using Volo.Abp.Account.Pro.Admin.Blazor.Server;
using Volo.Abp.Account.Pro.Public.Blazor.Server;
using Volo.Abp.Account.Public.Web;
using Volo.Abp.Account.Public.Web.ExternalProviders;
using Volo.Abp.Account.Web;
using Volo.Abp.Account.Public.Web.Impersonation;
using Volo.Abp.AspNetCore.Authentication.JwtBearer;
using Volo.Abp.AspNetCore.Components.Server.LeptonTheme;
using Volo.Abp.AspNetCore.Components.Server.LeptonTheme.Bundling;
using Volo.Abp.AspNetCore.Components.Web.Theming.Routing;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Lepton;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Lepton.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.AuditLogging.Blazor.Server;
using Volo.Abp.Autofac;
using Volo.Abp.AutoMapper;
using Volo.Abp.Gdpr.Blazor.Server;
using Volo.Abp.Identity;
using Volo.Abp.Identity.Pro.Blazor;
using Volo.Abp.Identity.Pro.Blazor.Server;
using Volo.Abp.IdentityServer.Blazor.Server;
using Volo.Abp.LanguageManagement.Blazor.Server;
using Volo.Abp.LeptonTheme.Management.Blazor.Server;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;
using Volo.Abp.TextTemplateManagement.Blazor.Server;
using Volo.Abp.UI.Navigation;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
using Volo.Saas.Host;
using Volo.Saas.Host.Blazor;
using Volo.Saas.Host.Blazor.Server;
using Volo.Abp.BackgroundJobs.Hangfire;
using Hangfire;
namespace Pol.Blazor;
[DependsOn(
typeof(PolApplicationModule),
typeof(PolEntityFrameworkCoreModule),
typeof(PolHttpApiModule),
typeof(AbpAspNetCoreMvcUiLeptonThemeModule),
typeof(AbpAutofacModule),
typeof(AbpSwashbuckleModule),
typeof(AbpAccountPublicWebImpersonationModule),
typeof(AbpAspNetCoreSerilogModule),
typeof(AbpAspNetCoreComponentsServerLeptonThemeModule),
typeof(AbpAccountPublicWebIdentityServerModule),
typeof(AbpAccountPublicBlazorServerModule),
typeof(AbpAccountAdminBlazorServerModule),
typeof(AbpAuditLoggingBlazorServerModule),
typeof(AbpIdentityProBlazorServerModule),
typeof(LeptonThemeManagementBlazorServerModule),
typeof(AbpIdentityServerBlazorServerModule),
typeof(LanguageManagementBlazorServerModule),
typeof(SaasHostBlazorServerModule),
typeof(TextTemplateManagementBlazorServerModule),
typeof(AbpGdprBlazorServerModule),
typeof(AbpBackgroundJobsHangfireModule)
)]
public class PolBlazorModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.PreConfigure(options =>
{
options.AddAssemblyResource(
typeof(PolResource),
typeof(PolDomainModule).Assembly,
typeof(PolDomainSharedModule).Assembly,
typeof(PolApplicationModule).Assembly,
typeof(PolApplicationContractsModule).Assembly,
typeof(PolBlazorModule).Assembly
);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration();
ConfigureUrls(configuration);
ConfigureBundles();
ConfigureAuthentication(context, configuration);
ConfigureImpersonation(context, configuration);
ConfigureAutoMapper();
ConfigureVirtualFileSystem(hostingEnvironment);
ConfigureLocalizationServices();
ConfigureSwaggerServices(context.Services);
ConfigureExternalProviders(context, configuration);
ConfigureAutoApiControllers();
ConfigureBlazorise(context);
ConfigureRouter(context);
ConfigureMenu(context);
ConfigureLeptonTheme();
ConfigureHangfire(context, configuration);
}
private void ConfigureHangfire(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddHangfire(config =>
{
config.UseSqlServerStorage(configuration.GetConnectionString("Hangfire"));
});
}
private void ConfigureUrls(IConfiguration configuration)
{
Configure(options =>
{
options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(','));
});
}
private void ConfigureBundles()
{
Configure(options =>
{
// MVC UI
options.StyleBundles.Configure(
LeptonThemeBundles.Styles.Global,
bundle =>
{
bundle.AddFiles("/global-styles.css");
}
);
// Blazor UI
options.StyleBundles.Configure(
BlazorLeptonThemeBundles.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("/Pol.Blazor.styles.css");
}
);
});
}
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication()
.AddJwtBearer(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
options.Audience = "Pol";
});
}
private void ConfigureImpersonation(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.Configure(options =>
{
options.EnableTenantImpersonation = true;
});
context.Services.Configure(options =>
{
options.EnableUserImpersonation = true;
});
context.Services.Configure(options =>
{
options.TenantAdminUserName = "admin";
options.ImpersonationTenantPermission = SaasHostPermissions.Tenants.Impersonation;
options.ImpersonationUserPermission = IdentityPermissions.Users.Impersonation;
});
}
private void ConfigureVirtualFileSystem(IWebHostEnvironment hostingEnvironment)
{
if (hostingEnvironment.IsDevelopment())
{
Configure(options =>
{
options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Pol.Domain.Shared"));
options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Pol.Domain"));
options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Pol.Application.Contracts"));
options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Pol.Application"));
options.FileSets.ReplaceEmbeddedByPhysical(hostingEnvironment.ContentRootPath);
});
}
}
private void ConfigureSwaggerServices(IServiceCollection services)
{
services.AddAbpSwaggerGen(
options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Pol API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
}
);
}
private void ConfigureExternalProviders(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication()
.AddGoogle(GoogleDefaults.AuthenticationScheme, _ => {})
.WithDynamicOptions(
GoogleDefaults.AuthenticationScheme,
options =>
{
options.WithProperty(x => x.ClientId);
options.WithProperty(x => x.ClientSecret, isSecret: true);
}
)
.AddMicrosoftAccount(MicrosoftAccountDefaults.AuthenticationScheme, options =>
{
//Personal Microsoft accounts as an example.
options.AuthorizationEndpoint = "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize";
options.TokenEndpoint = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token";
})
.WithDynamicOptions(
MicrosoftAccountDefaults.AuthenticationScheme,
options =>
{
options.WithProperty(x => x.ClientId);
options.WithProperty(x => x.ClientSecret, isSecret: true);
}
)
.AddTwitter(TwitterDefaults.AuthenticationScheme, options => options.RetrieveUserDetails = true)
.WithDynamicOptions(
TwitterDefaults.AuthenticationScheme,
options =>
{
options.WithProperty(x => x.ConsumerKey);
options.WithProperty(x => x.ConsumerSecret, isSecret: true);
}
);
}
private void ConfigureLocalizationServices()
{
Configure(options =>
{
options.Languages.Add(new LanguageInfo("ar", "ar", "العربية", "ae"));
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("hu", "hu", "Magyar"));
options.Languages.Add(new LanguageInfo("fi", "fi", "Finnish", "fi"));
options.Languages.Add(new LanguageInfo("fr", "fr", "Français", "fr"));
options.Languages.Add(new LanguageInfo("hi", "hi", "Hindi", "in"));
options.Languages.Add(new LanguageInfo("it", "it", "Italiano", "it"));
options.Languages.Add(new LanguageInfo("pt-BR", "pt-BR", "Português"));
options.Languages.Add(new LanguageInfo("ru", "ru", "Русский", "ru"));
options.Languages.Add(new LanguageInfo("sk", "sk", "Slovak", "sk"));
options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe"));
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
options.Languages.Add(new LanguageInfo("zh-Hant", "zh-Hant", "繁體中文"));
options.Languages.Add(new LanguageInfo("de-DE", "de-DE", "Deutsch", "de"));
options.Languages.Add(new LanguageInfo("es", "es", "Español"));
});
}
private void ConfigureBlazorise(ServiceConfigurationContext context)
{
context.Services
.AddBootstrap5Providers()
.AddFontAwesomeIcons();
}
private void ConfigureMenu(ServiceConfigurationContext context)
{
Configure(options =>
{
options.MenuContributors.Add(new PolMenuContributor());
});
}
private void ConfigureLeptonTheme()
{
Configure(options =>
{
options.FooterComponent = typeof(MainFooterComponent);
});
}
private void ConfigureRouter(ServiceConfigurationContext context)
{
Configure(options =>
{
options.AppAssembly = typeof(PolBlazorModule).Assembly;
});
}
private void ConfigureAutoApiControllers()
{
Configure(options =>
{
options.ConventionalControllers.Create(typeof(PolApplicationModule).Assembly);
});
}
private void ConfigureAutoMapper()
{
Configure(options =>
{
options.AddMaps();
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var env = context.GetEnvironment();
var app = context.GetApplicationBuilder();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAbpRequestLocalization();
if (!env.IsDevelopment())
{
app.UseErrorPage();
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", "Pol API");
});
app.UseAuditing();
app.UseAbpSerilogEnrichers();
app.UseConfiguredEndpoints();
app.UseHangfireDashboard("/hangfire");
}
}
Also, this is what shows up in the application logs:
4 Answer(s)
-
0
Hi, thanks for the detailed information. I'll check and write you back asap.
-
0
Hi, please add the
app.UseHangfireDashboard("/hangfire");
middleware before theapp.UseConfiguredEndpoints();
middleware:public override void OnApplicationInitialization(ApplicationInitializationContext context) { //... app.UseHangfireDashboard("/hangfire"); //should defined before UseConfiguredEndpoints() app.UseConfiguredEndpoints(); }
Then, you should be able to see the /hangfire page:
-
0
Thanks - that solved the problem.
I had been following this page of documentation:
https://docs.abp.io/en/abp/latest/Background-Jobs-Hangfire
I'd say it should be updated to add this requirement.
Jeff