SignalR in ASP.NET Core 2026: Comunicazione Real-Time, Hub e Domande da Colloquio
Padroneggiare SignalR per applicazioni web real-time in ASP.NET Core. Hub, gruppi, scalabilità con Redis e domande frequenti nei colloqui tecnici.

SignalR abilita la comunicazione bidirezionale in tempo reale tra server e client nelle applicazioni ASP.NET Core. A differenza dei tradizionali pattern HTTP request-response, SignalR mantiene connessioni persistenti che permettono ai server di inviare aggiornamenti ai client istantaneamente, elemento essenziale per applicazioni di chat, dashboard live, editing collaborativo e notifiche in tempo reale.
SignalR seleziona automaticamente il miglior trasporto disponibile: prima WebSockets, poi Server-Sent Events, infine Long Polling. Questo meccanismo di fallback garantisce compatibilità su tutti i browser e configurazioni di rete senza alcuna modifica al codice.
Come gli Hub SignalR Abilitano la Comunicazione Server-Client
Un Hub funziona come una pipeline ad alto livello che gestisce le invocazioni di metodi tra client e server. I client chiamano metodi sull'hub, e l'hub può invocare metodi sui client connessi. Questa astrazione elimina la complessità della gestione manuale delle connessioni WebSocket.
L'esempio seguente dimostra un hub di chat basilare che trasmette messaggi a tutti i client connessi:
using Microsoft.AspNetCore.SignalR;
public class ChatHub : Hub
{
// Called when a client sends a message
public async Task SendMessage(string user, string message)
{
// Broadcast to ALL connected clients
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
// Called automatically when a client connects
public override async Task OnConnectedAsync()
{
await Clients.Caller.SendAsync("Connected", Context.ConnectionId);
await base.OnConnectedAsync();
}
// Called automatically when a client disconnects
public override async Task OnDisconnectedAsync(Exception? exception)
{
// Clean up resources, notify other users, etc.
await base.OnDisconnectedAsync(exception);
}
}La proprietà Clients fornisce accesso a tutti i client connessi attraverso varie opzioni di targeting: All, Caller, Others, Group e User.
Configurazione di SignalR in ASP.NET Core 9
La registrazione di SignalR in ASP.NET Core 9 segue il pattern middleware standard. La configurazione seguente abilita i protocolli JSON e MessagePack con dimensioni buffer personalizzate:
var builder = WebApplication.CreateBuilder(args);
// Add SignalR services with configuration
builder.Services.AddSignalR(options =>
{
options.EnableDetailedErrors = builder.Environment.IsDevelopment();
options.MaximumReceiveMessageSize = 64 * 1024; // 64 KB
options.StreamBufferCapacity = 10;
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
})
.AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.PropertyNamingPolicy = null; // PascalCase
});
var app = builder.Build();
// Map the hub endpoint
app.MapHub<ChatHub>("/chathub");
app.Run();Le impostazioni KeepAliveInterval e ClientTimeoutInterval controllano il monitoraggio dello stato della connessione. I client che non rispondono entro il periodo di timeout vengono considerati disconnessi.
Gestione dei Gruppi per la Consegna Mirata dei Messaggi
I gruppi forniscono un meccanismo per organizzare le connessioni e inviare messaggi a sottoinsiemi di client. Un caso d'uso tipico coinvolge le chat room, dove gli utenti si uniscono a stanze specifiche e ricevono solo messaggi da quelle stanze.
public class GroupChatHub : Hub
{
// Add the current connection to a group
public async Task JoinRoom(string roomName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
// Notify the group that someone joined
await Clients.Group(roomName).SendAsync(
"UserJoined",
Context.User?.Identity?.Name ?? "Anonymous"
);
}
// Remove the current connection from a group
public async Task LeaveRoom(string roomName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
await Clients.Group(roomName).SendAsync(
"UserLeft",
Context.User?.Identity?.Name ?? "Anonymous"
);
}
// Send a message to a specific group only
public async Task SendToRoom(string roomName, string message)
{
await Clients.Group(roomName).SendAsync(
"ReceiveMessage",
Context.User?.Identity?.Name,
message
);
}
}L'appartenenza ai gruppi è legata alle connessioni, non agli utenti. Quando un utente si riconnette, deve rientrare nei suoi gruppi. Per un'appartenenza persistente ai gruppi, le associazioni devono essere memorizzate in un database e ripristinate automaticamente in OnConnectedAsync.
I gruppi esistono solo in memoria e non vengono persistiti attraverso riavvii del server o in scenari scalati senza backplane. Per deployment in produzione con più server, utilizzare Redis o Azure SignalR Service come backplane.
Protezione delle Connessioni SignalR con Autenticazione
SignalR si integra con l'autenticazione di ASP.NET Core. L'attributo [Authorize] limita l'accesso all'hub agli utenti autenticati, e Context.User fornisce accesso ai claim dell'utente autenticato.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
[Authorize] // Require authentication for the entire hub
public class SecureHub : Hub
{
// Access user claims through Context.User
public async Task SendPrivateMessage(string recipientUserId, string message)
{
var senderName = Context.User?.Identity?.Name
?? throw new HubException("User not authenticated");
// Send to a specific user (all their connections)
await Clients.User(recipientUserId).SendAsync(
"PrivateMessage",
senderName,
message
);
}
[Authorize(Roles = "Admin")] // Role-based authorization on method
public async Task BroadcastAnnouncement(string announcement)
{
await Clients.All.SendAsync("Announcement", announcement);
}
}Per le connessioni WebSocket, i token di autenticazione devono essere passati tramite query string poiché WebSocket non supporta header personalizzati. Il client JavaScript viene configurato per includere il token:
import * as signalR from "@microsoft/signalr";
const connection = new signalR.HubConnectionBuilder()
.withUrl("/securehub", {
accessTokenFactory: () => localStorage.getItem("authToken") || ""
})
.withAutomaticReconnect()
.build();
await connection.start();Sul server, l'autenticazione JWT viene configurata per leggere il token dalla query string:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// Read token from query string for SignalR
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
path.StartsWithSegments("/securehub"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});Pronto a superare i tuoi colloqui su .NET?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Scalabilità di SignalR con Redis Backplane
In un ambiente con load balancing, i client potrebbero connettersi a diverse istanze del server. Senza un backplane, i messaggi inviati da un server raggiungono solo i client connessi a quel server. Redis serve come backplane per sincronizzare i messaggi attraverso tutte le istanze del server.
builder.Services.AddSignalR()
.AddStackExchangeRedis(options =>
{
options.Configuration = builder.Configuration
.GetConnectionString("Redis");
// Optional: configure Redis-specific options
options.Configuration.ChannelPrefix =
RedisChannel.Literal("MyApp_SignalR_");
});Il backplane Redis pubblica tutti i messaggi SignalR sui canali Redis. Ogni istanza del server si iscrive a questi canali e consegna i messaggi ai suoi client locali. Questo approccio aggiunge una latenza minima (tipicamente 1-2ms) abilitando al contempo la scalabilità orizzontale.
Per deployment enterprise, Azure SignalR Service fornisce un'alternativa gestita che gestisce automaticamente scalabilità, gestione delle connessioni e disponibilità.
Hub Fortemente Tipizzati per Sicurezza a Compile-Time
Gli hub fortemente tipizzati sostituiscono le magic string con metodi di interfaccia, abilitando la verifica a compile-time delle chiamate ai metodi client:
public interface INotificationClient
{
Task ReceiveNotification(string title, string message, string severity);
Task UpdateProgress(int percentage);
Task TaskCompleted(Guid taskId, bool success);
}
// NotificationHub.cs
public class NotificationHub : Hub<INotificationClient>
{
public async Task NotifyAll(string title, string message)
{
// Compile-time checking - no magic strings
await Clients.All.ReceiveNotification(title, message, "info");
}
public async Task ReportProgress(string groupName, int percentage)
{
// IDE autocomplete works here
await Clients.Group(groupName).UpdateProgress(percentage);
}
}Il refactoring diventa più sicuro perché rinominare un metodo nell'interfaccia mostra immediatamente tutti i punti di chiamata che necessitano aggiornamenti.
Streaming di Dati con SignalR Channels
SignalR supporta lo streaming per scenari dove i dati vengono prodotti incrementalmente, come aggiornamenti di progresso, tailing dei log o dati di sensori in tempo reale. Sono supportati sia lo streaming server-to-client che client-to-server.
public class StreamingHub : Hub
{
// Server-to-client streaming with IAsyncEnumerable
public async IAsyncEnumerable<StockPrice> StreamStockPrices(
string[] symbols,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
var random = new Random();
while (!cancellationToken.IsCancellationRequested)
{
foreach (var symbol in symbols)
{
yield return new StockPrice
{
Symbol = symbol,
Price = random.NextDouble() * 1000,
Timestamp = DateTime.UtcNow
};
}
await Task.Delay(1000, cancellationToken);
}
}
// Client-to-server streaming
public async Task UploadStream(IAsyncEnumerable<LogEntry> stream)
{
await foreach (var entry in stream)
{
// Process each log entry as it arrives
await ProcessLogEntry(entry);
}
}
}
public record StockPrice(string Symbol, double Price, DateTime Timestamp);
public record LogEntry(string Level, string Message, DateTime Timestamp);Il client JavaScript consuma lo stream utilizzando un iteratore asincrono:
const stream = connection.stream("StreamStockPrices", ["AAPL", "MSFT"]);
stream.subscribe({
next: (price) => console.log(`${price.symbol}: $${price.price}`),
error: (err) => console.error(err),
complete: () => console.log("Stream completed")
});Domande Frequenti sui Colloqui SignalR
I colloqui tecnici trattano frequentemente l'architettura SignalR e scenari del mondo reale. Le seguenti domande appaiono regolarmente nelle sessioni di colloquio .NET:
Spiegare il processo di negoziazione del trasporto: SignalR tenta prima WebSockets, poi Server-Sent Events, poi Long Polling. Comprendere quando viene utilizzato ogni trasporto e le sue limitazioni.
D: Come gestisce SignalR lo stato della connessione quando un client perde temporaneamente la connettività?
SignalR mantiene lo stato della connessione sul server per un periodo configurabile. Con la riconnessione automatica abilitata, il client tenta di riconnettersi utilizzando lo stesso ID di connessione. In caso di successo, la connessione riprende senza perdere le appartenenze ai gruppi. Se la disconnessione supera il timeout, viene stabilita una nuova connessione e il client deve rientrare nei gruppi.
D: Qual è la differenza tra Clients.User() e Clients.Client()?
Clients.User(userId) mira a tutte le connessioni appartenenti a un utente autenticato specifico, utile quando un utente ha più tab del browser aperte. Clients.Client(connectionId) mira a una singola connessione specifica. Il targeting basato sull'utente richiede autenticazione e utilizza IUserIdProvider per mappare le connessioni agli ID utente.
D: Come si implementerebbe il rilevamento della presenza (mostrare chi è online)?
Tracciare le connessioni in OnConnectedAsync e OnDisconnectedAsync. Memorizzare le mappature utente-connessione in un dizionario concorrente o cache distribuita. Per deployment scalati, utilizzare uno storage condiviso come Redis. Trasmettere gli aggiornamenti di presenza ai gruppi rilevanti quando gli utenti si connettono o disconnettono.
D: Spiegare lo scopo di un backplane SignalR.
Un backplane sincronizza i messaggi attraverso più istanze del server in un ambiente con load balancing. Senza di esso, un messaggio inviato dal server A raggiunge solo i client connessi al server A. Redis e Azure SignalR Service sono opzioni di backplane comuni. Il backplane pubblica messaggi su un canale condiviso a cui tutti i server sono iscritti.
Strategie di Ottimizzazione delle Performance
Le performance di SignalR dipendono dalla dimensione dei messaggi, dalla frequenza e dal numero di connessioni. La documentazione delle best practice per le performance di ASP.NET Core fornisce benchmark dettagliati.
- Utilizzare il protocollo MessagePack per la serializzazione binaria, riduce la dimensione del payload del 30-50% rispetto a JSON
- Raggruppare i messaggi quando possibile invece di inviare molti messaggi piccoli
- Limitare le dimensioni dei gruppi a migliaia di membri; per audience più ampie, considerare pattern pub/sub
- Abilitare la compressione per payload pesanti di testo
- Impostare timeout appropriati per rilasciare prontamente le risorse dalle connessioni obsolete
// MessagePack configuration for smaller payloads
builder.Services.AddSignalR()
.AddMessagePackProtocol(options =>
{
options.SerializerOptions =
MessagePackSerializerOptions.Standard
.WithCompression(MessagePackCompression.Lz4Block);
});Conclusione
- SignalR astrae la selezione del trasporto (WebSockets, SSE, Long Polling) e fornisce un modello di programmazione consistente per la comunicazione real-time
- Gli hub centralizzano la gestione delle connessioni; gli hub fortemente tipizzati aggiungono sicurezza a compile-time per le chiamate ai metodi client
- I gruppi abilitano la consegna mirata dei messaggi a sottoinsiemi di connessioni, necessario rientrare nei gruppi dopo la riconnessione
- L'autenticazione si integra con ASP.NET Core Identity; passare i token tramite query string per le connessioni WebSocket
- Scalare orizzontalmente con backplane Redis o Azure SignalR Service per sincronizzare i messaggi attraverso le istanze del server
- Lo streaming supporta la consegna incrementale dei dati con
IAsyncEnumerablesia per scenari server-to-client che client-to-server
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Scritto da
Anthony Fillion-MailletSviluppatore fullstack, fondatore di SharpSkill
Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.
Aggiornato il 12 agosto 2026
Condividi
Articoli correlati

ASP.NET Core Minimal APIs nel 2026: Architettura, Prestazioni e Domande per Colloqui
Le Minimal APIs in ASP.NET Core offrono un approccio snello per creare endpoint HTTP con meno codice boilerplate, supporto Native AOT e prestazioni ottimizzate per microservizi e deployment serverless.

.NET 10 nel 2026: Nuove Funzionalita, Native AOT e Domande per Colloqui Tecnici
.NET 10 come versione LTS introduce la compilazione Native AOT pronta per la produzione, C# 14 con Extension Members e la keyword field, applicazioni basate su file e filtri query nominati in EF Core 10.

.NET MAUI nel 2026: Sviluppo Cross-Platform e Domande per Colloqui Tecnici
Tutorial .NET MAUI per il 2026: sviluppare app cross-platform con .NET 10, handlers, MVVM, HybridWebView. Include le domande di colloquio più frequenti con risposte.