From 0b40f2c0f1e36fb4c0834f6ab730e252878cbdea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SAL=C4=B0H=20=C3=96ZKARA?= Date: Tue, 10 Mar 2026 15:15:26 +0300 Subject: [PATCH] Use Abp.AnonymousJob name and add validation Rename the internal anonymous transport job name from "AnonymousJob" to "Abp.AnonymousJob" and update docs accordingly. Add null/whitespace argument checks (Check.NotNull / Check.NotNullOrWhiteSpace) in anonymous handler registration and lookup APIs. Change background job managers (Hangfire, Quartz, RabbitMQ, TickerQ, Default) to only wrap jobs as anonymous when an anonymous handler is registered and there is no typed job configuration (check via BackgroundJobOptions.GetJobOrNull). Wire AbpBackgroundJobOptions into affected managers and update tests with a new case verifying that a typed job prevents anonymous wrapping. --- .../infrastructure/background-jobs/hangfire.md | 2 +- .../infrastructure/background-jobs/index.md | 2 +- .../BackgroundJobs/AbpBackgroundJobOptions.cs | 2 ++ .../Volo/Abp/BackgroundJobs/AnonymousJobArgs.cs | 4 ++-- .../AnonymousJobHandlerRegistry.cs | 6 ++++++ .../Hangfire/HangfireBackgroundJobManager.cs | 4 +++- .../Quartz/QuartzBackgroundJobManager.cs | 13 +++++++++++-- .../RabbitMQ/RabbitMqBackgroundJobManager.cs | 13 +++++++++++-- .../TickerQ/AbpTickerQBackgroundJobManager.cs | 4 +++- .../DefaultBackgroundJobManager.cs | 4 +++- .../BackgroundJobManager_Tests.cs | 17 +++++++++++++++++ 11 files changed, 60 insertions(+), 11 deletions(-) diff --git a/docs/en/framework/infrastructure/background-jobs/hangfire.md b/docs/en/framework/infrastructure/background-jobs/hangfire.md index 49f280f1f1..3ea466d83b 100644 --- a/docs/en/framework/infrastructure/background-jobs/hangfire.md +++ b/docs/en/framework/infrastructure/background-jobs/hangfire.md @@ -149,7 +149,7 @@ namespace MyProject Hangfire Dashboard provides information about your background jobs, including method names and serialized arguments as well as gives you an opportunity to manage them by performing different actions – retry, delete, trigger, etc. So it is important to restrict access to the Dashboard. To make it secure by default, only local requests are allowed, however you can change this by following the [official documentation](http://docs.hangfire.io/en/latest/configuration/using-dashboard.html) of Hangfire. -When you enqueue jobs via anonymous handlers (`IBackgroundJobManager.EnqueueAsync(string jobName, object args)` + `IAnonymousJobHandlerRegistry`), ABP uses an internal transport job name (`AnonymousJob`), but the Hangfire dashboard display name tries to show the effective job name from payload (for example, `ProcessOrder`). +When you enqueue jobs via anonymous handlers (`IBackgroundJobManager.EnqueueAsync(string jobName, object args)` + `IAnonymousJobHandlerRegistry`), ABP uses an internal transport job name (`Abp.AnonymousJob`), but the Hangfire dashboard display name tries to show the effective job name from payload (for example, `ProcessOrder`). You can integrate the Hangfire dashboard to [ABP authorization system](../../fundamentals/authorization/index.md) using the **AbpHangfireAuthorizationFilter** class. This class is defined in the `Volo.Abp.Hangfire` package. The following example, checks if the current user is logged in to the application: diff --git a/docs/en/framework/infrastructure/background-jobs/index.md b/docs/en/framework/infrastructure/background-jobs/index.md index 19a371266f..61a4171b49 100644 --- a/docs/en/framework/infrastructure/background-jobs/index.md +++ b/docs/en/framework/infrastructure/background-jobs/index.md @@ -261,7 +261,7 @@ Then enqueue it by name: await _backgroundJobManager.EnqueueAsync("ProcessOrder", new { OrderId = "42" }); ``` -ABP keeps a stable internal transport job name (`AnonymousJob`) for provider compatibility, while preserving your effective job name (`ProcessOrder`) in the payload. +ABP keeps a stable internal transport job name (`Abp.AnonymousJob`) for provider compatibility, while preserving your effective job name (`ProcessOrder`) in the payload. ### Disable Job Execution diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs index cb2f3853c1..f98d2d89f4 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs @@ -96,6 +96,8 @@ public class AbpBackgroundJobOptions public void AddAnonymousJobHandler(string jobName, Action handler) { + Check.NotNull(handler, nameof(handler)); + AddAnonymousJobHandler(jobName, (context, ct) => { handler(context, ct); diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobArgs.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobArgs.cs index 09bac99878..09f9dc0f9f 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobArgs.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobArgs.cs @@ -1,9 +1,9 @@ namespace Volo.Abp.BackgroundJobs; -[BackgroundJobName("AnonymousJob")] +[BackgroundJobName(JobNameConstant)] public class AnonymousJobArgs { - public const string JobNameConstant = "AnonymousJob"; + public const string JobNameConstant = "Abp.AnonymousJob"; public string JobName { get; } diff --git a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobHandlerRegistry.cs b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobHandlerRegistry.cs index bb36a8d454..24810d7de9 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobHandlerRegistry.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AnonymousJobHandlerRegistry.cs @@ -27,6 +27,8 @@ public class AnonymousJobHandlerRegistry : IAnonymousJobHandlerRegistry, ISingle public virtual void Register(string jobName, Action handler) { + Check.NotNull(handler, nameof(handler)); + Register(jobName, (context, ct) => { handler(context, ct); @@ -36,16 +38,20 @@ public class AnonymousJobHandlerRegistry : IAnonymousJobHandlerRegistry, ISingle public virtual bool Unregister(string jobName) { + Check.NotNullOrWhiteSpace(jobName, nameof(jobName)); return _handlers.TryRemove(jobName, out _); } public virtual bool IsRegistered(string jobName) { + Check.NotNullOrWhiteSpace(jobName, nameof(jobName)); return _handlers.ContainsKey(jobName) || _options.IsAnonymousJobRegistered(jobName); } public virtual Func? Get(string jobName) { + Check.NotNullOrWhiteSpace(jobName, nameof(jobName)); + if (_handlers.TryGetValue(jobName, out var handler)) { return handler; diff --git a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs index 67406f2b8d..9d84f1f329 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs @@ -77,7 +77,9 @@ public class HangfireBackgroundJobManager : IBackgroundJobManager, ITransientDep protected virtual bool ShouldWrapAsAnonymousJob(string jobName) { - return jobName != AnonymousJobArgs.JobNameConstant && AnonymousJobHandlerRegistry.IsRegistered(jobName); + return jobName != AnonymousJobArgs.JobNameConstant && + AnonymousJobHandlerRegistry.IsRegistered(jobName) && + BackgroundJobOptions.Value.GetJobOrNull(jobName) == null; } protected virtual string GetQueueName(Type argsType) diff --git a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs index 4d52498f84..a30b8bc9dd 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs @@ -18,17 +18,24 @@ public class QuartzBackgroundJobManager : IBackgroundJobManager, ITransientDepen protected IScheduler Scheduler { get; } protected AbpBackgroundJobQuartzOptions Options { get; } + protected AbpBackgroundJobOptions BackgroundJobOptions { get; } protected IJsonSerializer JsonSerializer { get; } protected IAnonymousJobHandlerRegistry AnonymousJobHandlerRegistry { get; } public ILogger Logger { get; set; } - public QuartzBackgroundJobManager(IScheduler scheduler, IOptions options, IJsonSerializer jsonSerializer, IAnonymousJobHandlerRegistry anonymousJobHandlerRegistry) + public QuartzBackgroundJobManager( + IScheduler scheduler, + IOptions options, + IOptions backgroundJobOptions, + IJsonSerializer jsonSerializer, + IAnonymousJobHandlerRegistry anonymousJobHandlerRegistry) { Scheduler = scheduler; JsonSerializer = jsonSerializer; Options = options.Value; + BackgroundJobOptions = backgroundJobOptions.Value; AnonymousJobHandlerRegistry = anonymousJobHandlerRegistry; Logger = NullLogger.Instance; } @@ -59,7 +66,9 @@ public class QuartzBackgroundJobManager : IBackgroundJobManager, ITransientDepen protected virtual bool ShouldWrapAsAnonymousJob(string jobName) { - return jobName != AnonymousJobArgs.JobNameConstant && AnonymousJobHandlerRegistry.IsRegistered(jobName); + return jobName != AnonymousJobArgs.JobNameConstant && + AnonymousJobHandlerRegistry.IsRegistered(jobName) && + BackgroundJobOptions.GetJobOrNull(jobName) == null; } public virtual async Task ReEnqueueAsync(TArgs args, int retryCount, int retryIntervalMillisecond, diff --git a/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/RabbitMqBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/RabbitMqBackgroundJobManager.cs index 5483a3094e..71510e83ff 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/RabbitMqBackgroundJobManager.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/RabbitMqBackgroundJobManager.cs @@ -2,6 +2,7 @@ using System; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.Json; @@ -12,13 +13,19 @@ public class RabbitMqBackgroundJobManager : IBackgroundJobManager, ITransientDep { protected IJobQueueManager JobQueueManager { get; } protected IAnonymousJobHandlerRegistry AnonymousJobHandlerRegistry { get; } + protected AbpBackgroundJobOptions BackgroundJobOptions { get; } protected IJsonSerializer JsonSerializer { get; } public ILogger Logger { get; set; } - public RabbitMqBackgroundJobManager(IJobQueueManager jobQueueManager, IAnonymousJobHandlerRegistry anonymousJobHandlerRegistry, IJsonSerializer jsonSerializer) + public RabbitMqBackgroundJobManager( + IJobQueueManager jobQueueManager, + IAnonymousJobHandlerRegistry anonymousJobHandlerRegistry, + IOptions backgroundJobOptions, + IJsonSerializer jsonSerializer) { JobQueueManager = jobQueueManager; AnonymousJobHandlerRegistry = anonymousJobHandlerRegistry; + BackgroundJobOptions = backgroundJobOptions.Value; JsonSerializer = jsonSerializer; Logger = NullLogger.Instance; } @@ -56,6 +63,8 @@ public class RabbitMqBackgroundJobManager : IBackgroundJobManager, ITransientDep protected virtual bool ShouldWrapAsAnonymousJob(string jobName) { - return jobName != AnonymousJobArgs.JobNameConstant && AnonymousJobHandlerRegistry.IsRegistered(jobName); + return jobName != AnonymousJobArgs.JobNameConstant && + AnonymousJobHandlerRegistry.IsRegistered(jobName) && + BackgroundJobOptions.GetJobOrNull(jobName) == null; } } diff --git a/framework/src/Volo.Abp.BackgroundJobs.TickerQ/Volo/Abp/BackgroundJobs/TickerQ/AbpTickerQBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs.TickerQ/Volo/Abp/BackgroundJobs/TickerQ/AbpTickerQBackgroundJobManager.cs index 54b97adf30..82df9e2fe7 100644 --- a/framework/src/Volo.Abp.BackgroundJobs.TickerQ/Volo/Abp/BackgroundJobs/TickerQ/AbpTickerQBackgroundJobManager.cs +++ b/framework/src/Volo.Abp.BackgroundJobs.TickerQ/Volo/Abp/BackgroundJobs/TickerQ/AbpTickerQBackgroundJobManager.cs @@ -65,7 +65,9 @@ public class AbpTickerQBackgroundJobManager : IBackgroundJobManager, ITransientD protected virtual bool ShouldWrapAsAnonymousJob(string jobName) { - return jobName != AnonymousJobArgs.JobNameConstant && AnonymousJobHandlerRegistry.IsRegistered(jobName); + return jobName != AnonymousJobArgs.JobNameConstant && + AnonymousJobHandlerRegistry.IsRegistered(jobName) && + Options.GetJobOrNull(jobName) == null; } protected virtual async Task EnqueueAsync(BackgroundJobConfiguration job, object args, BackgroundJobPriority priority, TimeSpan? delay) diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/DefaultBackgroundJobManager.cs b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/DefaultBackgroundJobManager.cs index 5669525db9..4714e11e72 100644 --- a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/DefaultBackgroundJobManager.cs +++ b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/DefaultBackgroundJobManager.cs @@ -87,6 +87,8 @@ public class DefaultBackgroundJobManager : IBackgroundJobManager, ITransientDepe protected virtual bool ShouldWrapAsAnonymousJob(string jobName) { - return jobName != AnonymousJobArgs.JobNameConstant && AnonymousJobHandlerRegistry.IsRegistered(jobName); + return jobName != AnonymousJobArgs.JobNameConstant && + AnonymousJobHandlerRegistry.IsRegistered(jobName) && + BackgroundJobOptions.Value.GetJobOrNull(jobName) == null; } } diff --git a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobManager_Tests.cs b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobManager_Tests.cs index bb8d05bf86..948c1b9f4a 100644 --- a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobManager_Tests.cs +++ b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo/Abp/BackgroundJobs/BackgroundJobManager_Tests.cs @@ -10,11 +10,13 @@ public class BackgroundJobManager_Tests : BackgroundJobsTestBase { private readonly IBackgroundJobManager _backgroundJobManager; private readonly IBackgroundJobStore _backgroundJobStore; + private readonly IAnonymousJobHandlerRegistry _anonymousJobHandlerRegistry; public BackgroundJobManager_Tests() { _backgroundJobManager = GetRequiredService(); _backgroundJobStore = GetRequiredService(); + _anonymousJobHandlerRegistry = GetRequiredService(); } [Fact] @@ -75,4 +77,19 @@ public class BackgroundJobManager_Tests : BackgroundJobsTestBase jobInfo.JobArgs.ShouldContain("TestAnonymousJob"); jobInfo.JobArgs.ShouldContain("ORD-001"); } + + [Fact] + public async Task Should_Not_Wrap_If_Typed_Job_Exists_For_Same_Name() + { + var typedJobName = BackgroundJobNameAttribute.GetName(); + _anonymousJobHandlerRegistry.Register(typedJobName, (_, _) => Task.CompletedTask); + + var jobIdAsString = await _backgroundJobManager.EnqueueAsync(typedJobName, new { Value = "42" }); + jobIdAsString.ShouldNotBe(default); + + var jobInfo = await _backgroundJobStore.FindAsync(Guid.Parse(jobIdAsString)); + jobInfo.ShouldNotBeNull(); + jobInfo.JobName.ShouldBe(typedJobName); + jobInfo.JobName.ShouldNotBe(AnonymousJobArgs.JobNameConstant); + } }