| name | add-signalr-realtime |
| description | Add real-time SignalR features to an ABP app via the Volo.Abp.AspNetCore.SignalR module — creating hubs, wiring conventional hub routes/authorization, reading CurrentUser in a hub, and connecting from a JS or Blazor/.NET client. USE FOR: installing the SignalR module, deriving from `AbpHub` (optionally strongly-typed with a client interface), the conventional `/signalr-hubs/...` route and `HubRoute` override, `[Authorize]` + `AbpSignalRUserIdProvider`/`Clients.User`, opting out of auto DI with `[DisableConventionalRegistration]` and auto hub mapping with `[DisableAutoHubMap]` (then configuring manually via `AbpSignalROptions`), and JS/Blazor client connection code. DO NOT USE FOR: distributed cache/lock or event-bus-based messaging (use distributed-caching-and-locking / background-jobs-and-events); Dapr pub/sub or inter-service communication (use integrate-dapr-services / design-module-and-service-communication); non-realtime HTTP endpoints (use expose-http-apis).
|
| license | MIT |
Add SignalR Real-Time to an ABP App
ABP's Volo.Abp.AspNetCore.SignalR module wraps ASP.NET Core SignalR so you don't
have to call services.AddSignalR() or map hub endpoints manually — ABP auto-registers
every hub in DI (as transient) and maps its endpoint conventionally.
When to Use
- Adding server-push / real-time messaging (chat, notifications, live updates) to an ABP app.
- Creating a hub that needs ABP's
CurrentUser / CurrentTenant / localization.
- Securing a hub with
[Authorize] and targeting connected users by id.
- Connecting to a hub from a JavaScript (MVC/Razor) or Blazor/.NET client.
When Not to Use
- Cross-instance/background messaging that isn't client-facing — use background-jobs-and-events (distributed event bus).
- Dapr pub/sub or inter-microservice messaging — use integrate-dapr-services / design-module-and-service-communication.
- Plain request/response HTTP endpoints — use expose-http-apis.
- Note: hosting/scaling/Redis-backplane/Azure SignalR concerns are standard ASP.NET Core (Microsoft's SignalR docs), not ABP-specific.
1. Install the module
Add the package to the web/API layer that hosts your hubs:
abp add-package Volo.Abp.AspNetCore.SignalR
Or add the NuGet package Volo.Abp.AspNetCore.SignalR and depend on the module:
[DependsOn(
typeof(AbpAspNetCoreSignalRModule)
)]
public class MyWebModule : AbpModule
{
}
You do not need services.AddSignalR() or a manual MapHub<T>(...) — the module
calls AddSignalR() and registers each hub's mapping into AbpEndpointRouterOptions. It
does not call UseEndpoints itself, though: your host's request pipeline must include
app.UseConfiguredEndpoints() (the startup templates already have it), which is where ABP
runs those hub mappings. If that call is missing, no hub endpoints get mapped.
2. Create a hub
Derive from AbpHub (or AbpHub<TClient> for a strongly-typed client) instead of the
plain Hub / Hub<T>. AbpHub adds useful base members: CurrentUser, CurrentTenant,
Logger, Clock, AuthorizationService, and localization via L.
using Microsoft.AspNetCore.Authorization;
using Volo.Abp;
using Volo.Abp.AspNetCore.SignalR;
[Authorize]
public class MessagingHub : AbpHub
{
public async Task SendMessage(Guid targetUserId, string message)
{
Check.NotNullOrWhiteSpace(message, nameof(message));
var senderUserName = CurrentUser.UserName;
Logger.LogInformation("Message from {User}", senderUserName);
await Clients.User(targetUserId.ToString())
.SendAsync("ReceiveMessage", senderUserName, message);
}
}
Strongly-typed client variant:
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
[Authorize]
public class ChatHub : AbpHub<IChatClient>
{
public Task SendMessage(string message)
{
Check.NotNullOrWhiteSpace(message, nameof(message));
return Clients.All.ReceiveMessage(CurrentUser.UserName!, message);
}
}
A plain Hub also works with ABP's auto-registration; AbpHub just gives you the
extra base properties without constructor injection.
3. Hub route (URL)
ABP maps the endpoint conventionally. For a hub named MessagingHub the route is:
/signalr-hubs/messaging
Rule (from HubRouteAttribute.GetRoutePattern): prefix /signalr-hubs/, then the hub
type name with the Hub suffix removed, converted to kebab-case.
Override the route with the HubRoute attribute:
[HubRoute("/my-messaging-hub")]
public class MessagingHub : AbpHub
{
}
4. Authorization and CurrentUser
Because ABP registers IUserIdProvider (AbpSignalRUserIdProvider) from ICurrentUser,
the hub is integrated with your app's authentication. Protect a hub with the standard
[Authorize] attribute:
using Microsoft.AspNetCore.Authorization;
[Authorize]
public class MessagingHub : AbpHub
{
public async Task SendPrivate(string toUserId, string message)
{
Check.NotNullOrWhiteSpace(message, nameof(message));
await Clients.User(toUserId).SendAsync("ReceiveMessage", CurrentUser.UserName, message);
}
}
Inside a hub, CurrentUser reflects the authenticated connection:
CurrentUser.IsAuthenticated
CurrentUser.Id (a Guid?)
CurrentUser.UserName
CurrentUser.TenantId (also available via CurrentTenant)
For permission checks you can use the base AuthorizationService or an
[Authorize("MyPermission")] policy on the hub/method.
5. Manual registration / mapping (optional)
Auto DI registration + auto mapping is the default. To opt out of auto DI registration,
put [DisableConventionalRegistration] on the hub, then register it yourself:
context.Services.AddTransient<MessagingHub>();
To keep DI registration but do the endpoint mapping yourself, add
[DisableAutoHubMap] and configure via AbpSignalROptions:
Configure<AbpSignalROptions>(options =>
{
options.Hubs.Add(
new HubConfig(
typeof(MessagingHub),
"/my-messaging/route",
hubOptions =>
{
hubOptions.LongPolling.PollTimeout = TimeSpan.FromSeconds(30);
}
)
);
});
To tweak a hub defined in a depended module you don't own, use options.Hubs.AddOrUpdate(...)
with a config lambda that sets config.RoutePattern and adds to config.ConfigureActions.
6. Connect from a client
JavaScript (MVC / Razor Pages)
Add the client package and include the script:
abp add-package @abp/signalr
abp install-libs
Include the script in your page/view (requires
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.SignalR):
@section scripts {
<abp-script type="typeof(SignalRBrowserScriptContributor)" />
}
Then use the standard SignalR JS client:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/signalr-hubs/messaging")
.build();
connection.on("ReceiveMessage", (user, message) => { });
await connection.start();
await connection.invoke("SendMessage", "3fa85f64-5717-4562-b3fc-2c963f66afa6", "Hello");
Blazor / .NET client
Use the standard HubConnectionBuilder from Microsoft.AspNetCore.SignalR.Client,
pointing at the conventional route:
var connection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/signalr-hubs/messaging"))
.Build();
connection.On<string, string>("ReceiveMessage", (user, message) => { });
await connection.StartAsync();
For an authenticated hub, pass the access token on the client via
options.AccessTokenProvider in WithUrl per the standard Microsoft SignalR client docs.
Sending from outside a hub
To send from outside a hub (e.g. an app service), inject
IHubContext<MessagingHub> (standard ASP.NET Core) and call
hubContext.Clients...SendAsync(...).
Validation
- Confirm the hub endpoint is reachable at its conventional route (
/signalr-hubs/<kebab-name-without-Hub-suffix>, or the HubRoute override).
- Verify the host pipeline includes
app.UseConfiguredEndpoints() — without it no hub endpoints are mapped.
- Start a client (
connection.start() / StartAsync()) and confirm messages round-trip via the registered on/On handler.
- For an
[Authorize] hub, confirm CurrentUser.IsAuthenticated/CurrentUser.Id reflect the connection and Clients.User(id) delivers to that user.
Common Pitfalls
- Missing
app.UseConfiguredEndpoints(). The module registers hub mappings but does not call UseEndpoints itself — if that pipeline call is absent, no hub endpoints get mapped (startup templates include it).
- Manually calling
AddSignalR() / MapHub<T>(). The module already does both; hubs are auto-registered (transient) and mapped conventionally.
- Wrong route. The conventional route strips the
Hub suffix and kebab-cases the name (MessagingHub → /signalr-hubs/messaging); override with [HubRoute].
- Opting out incorrectly. Use
[DisableConventionalRegistration] to skip auto DI (then register yourself), or [DisableAutoHubMap] + AbpSignalROptions to map yourself — they control different things.
- Treating hosting/scaling as ABP-specific. Redis backplane, Azure SignalR, and scale-out follow standard ASP.NET Core SignalR guidance, not this skill.