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<AppUrlOptions>(options =>
{
options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(','));
});
}
private void ConfigureBundles()
{
Configure<AbpBundlingOptions>(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<SaasHostBlazorOptions>(options =>
{
options.EnableTenantImpersonation = true;
});
context.Services.Configure<AbpIdentityProBlazorOptions>(options =>
{
options.EnableUserImpersonation = true;
});
context.Services.Configure<AbpAccountOptions>(options =>
{
options.TenantAdminUserName = "admin";
options.ImpersonationTenantPermission = SaasHostPermissions.Tenants.Impersonation;
options.ImpersonationUserPermission = IdentityPermissions.Users.Impersonation;
});
}
private void ConfigureVirtualFileSystem(IWebHostEnvironment hostingEnvironment)
{
if (hostingEnvironment.IsDevelopment())
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.ReplaceEmbeddedByPhysical<PolDomainSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Pol.Domain.Shared"));
options.FileSets.ReplaceEmbeddedByPhysical<PolDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Pol.Domain"));
options.FileSets.ReplaceEmbeddedByPhysical<PolApplicationContractsModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Pol.Application.Contracts"));
options.FileSets.ReplaceEmbeddedByPhysical<PolApplicationModule>(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Pol.Application"));
options.FileSets.ReplaceEmbeddedByPhysical<PolBlazorModule>(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<GoogleOptions, GoogleHandler>(
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<MicrosoftAccountOptions, MicrosoftAccountHandler>(
MicrosoftAccountDefaults.AuthenticationScheme,
options =>
{
options.WithProperty(x => x.ClientId);
options.WithProperty(x => x.ClientSecret, isSecret: true);
}
)
.AddTwitter(TwitterDefaults.AuthenticationScheme, options => options.RetrieveUserDetails = true)
.WithDynamicOptions<TwitterOptions, TwitterHandler>(
TwitterDefaults.AuthenticationScheme,
options =>
{
options.WithProperty(x => x.ConsumerKey);
options.WithProperty(x => x.ConsumerSecret, isSecret: true);
}
);
}
private void ConfigureLocalizationServices()
{
Configure<AbpLocalizationOptions>(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<AbpNavigationOptions>(options =>
{
options.MenuContributors.Add(new PolMenuContributor());
});
}
private void ConfigureLeptonTheme()
{
Configure<Volo.Abp.AspNetCore.Components.Web.LeptonTheme.LeptonThemeOptions>(options =>
{
options.FooterComponent = typeof(MainFooterComponent);
});
}
private void ConfigureRouter(ServiceConfigurationContext context)
{
Configure<AbpRouterOptions>(options =>
{
options.AppAssembly = typeof(PolBlazorModule).Assembly;
});
}
private void ConfigureAutoApiControllers()
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(PolApplicationModule).Assembly);
});
}
private void ConfigureAutoMapper()
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<PolBlazorModule>();
});
}
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