SignalR dans ASP.NET Core 2026 : Communication Temps Réel, Hubs et Questions d'Entretien
Guide complet de SignalR pour les applications web temps réel en ASP.NET Core. Hubs, groupes, authentification, montée en charge avec Redis et questions d'entretien technique.

SignalR permet la communication bidirectionnelle en temps réel entre serveurs et clients dans les applications ASP.NET Core. Contrairement aux schémas classiques requête-réponse HTTP, SignalR maintient des connexions persistantes qui permettent aux serveurs d'envoyer instantanément des mises à jour aux clients—une fonctionnalité essentielle pour les applications de chat, les tableaux de bord en direct, l'édition collaborative et les notifications temps réel.
SignalR sélectionne automatiquement le meilleur transport disponible : WebSockets en priorité, puis Server-Sent Events, puis Long Polling. Ce mécanisme de repli garantit la compatibilité avec tous les navigateurs et configurations réseau sans aucune modification du code.
Fonctionnement des Hubs SignalR pour la Communication Serveur-Client
Un Hub agit comme un pipeline de haut niveau qui gère les invocations de méthodes entre clients et serveurs. Les clients appellent des méthodes sur le hub, et le hub peut invoquer des méthodes sur les clients connectés. Cette abstraction élimine la complexité de la gestion manuelle des connexions WebSocket.
L'exemple suivant illustre un hub de chat basique qui diffuse les messages à tous les clients connectés :
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 propriété Clients donne accès à tous les clients connectés via différentes options de ciblage : All, Caller, Others, Group et User.
Configuration de SignalR dans ASP.NET Core 9
L'enregistrement de SignalR dans ASP.NET Core 9 suit le schéma standard des middlewares. La configuration ci-dessous active les protocoles JSON et MessagePack avec des tailles de buffer personnalisées :
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();Les paramètres KeepAliveInterval et ClientTimeoutInterval contrôlent la surveillance de l'état des connexions. Les clients qui ne répondent pas dans le délai imparti sont considérés comme déconnectés.
Gestion des Groupes pour une Diffusion Ciblée des Messages
Les groupes fournissent un mécanisme pour organiser les connexions et envoyer des messages à des sous-ensembles de clients. Un cas d'utilisation typique concerne les salons de discussion, où les utilisateurs rejoignent des salons spécifiques et ne reçoivent que les messages de ces salons.
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'appartenance à un groupe est liée aux connexions, pas aux utilisateurs. Lorsqu'un utilisateur se reconnecte, il doit rejoindre à nouveau ses groupes. Pour une appartenance persistante aux groupes, il convient de stocker les associations de groupes dans une base de données et de les rejoindre automatiquement dans OnConnectedAsync.
Les groupes n'existent qu'en mémoire et ne sont pas persistés lors des redémarrages du serveur ou dans les scénarios de montée en charge sans backplane. Pour les déploiements en production avec plusieurs serveurs, Redis ou Azure SignalR Service doit être utilisé comme backplane.
Sécurisation des Connexions SignalR avec l'Authentification
SignalR s'intègre avec l'authentification ASP.NET Core. L'attribut [Authorize] restreint l'accès au hub aux utilisateurs authentifiés, et Context.User donne accès aux claims de l'utilisateur authentifié.
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);
}
}Pour les connexions WebSocket, les tokens d'authentification doivent être passés via la chaîne de requête car les WebSockets ne supportent pas les en-têtes personnalisés. La configuration du client JavaScript pour inclure le token se présente ainsi :
import * as signalR from "@microsoft/signalr";
const connection = new signalR.HubConnectionBuilder()
.withUrl("/securehub", {
accessTokenFactory: () => localStorage.getItem("authToken") || ""
})
.withAutomaticReconnect()
.build();
await connection.start();Côté serveur, l'authentification JWT doit être configurée pour lire le token depuis la chaîne de requête :
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;
}
};
});Prêt à réussir tes entretiens .NET ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Montée en Charge de SignalR avec un Backplane Redis
Dans un environnement à répartition de charge, les clients peuvent se connecter à différentes instances de serveur. Sans backplane, les messages envoyés depuis un serveur n'atteignent que les clients connectés à ce serveur. Redis sert de backplane pour synchroniser les messages entre toutes les instances de serveur.
builder.Services.AddSignalR()
.AddStackExchangeRedis(options =>
{
options.Configuration = builder.Configuration
.GetConnectionString("Redis");
// Optional: configure Redis-specific options
options.Configuration.ChannelPrefix =
RedisChannel.Literal("MyApp_SignalR_");
});Le backplane Redis publie tous les messages SignalR sur des canaux Redis. Chaque instance de serveur s'abonne à ces canaux et délivre les messages à ses clients locaux. Cette approche ajoute une latence minimale (généralement 1-2ms) tout en permettant la montée en charge horizontale.
Pour les déploiements en entreprise, Azure SignalR Service offre une alternative managée qui gère automatiquement la montée en charge, la gestion des connexions et la disponibilité.
Hubs Fortement Typés pour la Sécurité à la Compilation
Les hubs fortement typés remplacent les chaînes magiques par des méthodes d'interface, permettant la vérification à la compilation des appels de méthodes clients :
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);
}
}Le refactoring devient plus sûr car renommer une méthode dans l'interface montre immédiatement tous les sites d'appel nécessitant une mise à jour.
Streaming de Données avec les Canaux SignalR
SignalR supporte le streaming pour les scénarios où les données sont produites de manière incrémentale, comme les mises à jour de progression, le suivi de logs ou les données de capteurs en temps réel. Le streaming serveur-vers-client et client-vers-serveur sont tous deux supportés.
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);Le client JavaScript consomme le stream en utilisant un itérateur asynchrone :
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")
});Questions d'Entretien Courantes sur SignalR
Les entretiens techniques abordent fréquemment l'architecture SignalR et les scénarios du monde réel. Les questions suivantes apparaissent régulièrement lors des sessions d'entretien .NET :
Il est important d'expliquer le processus de négociation de transport : SignalR tente d'abord WebSockets, puis se replie sur Server-Sent Events, puis Long Polling. Comprendre quand chaque transport est utilisé et ses limitations est essentiel.
Q : Comment SignalR gère-t-il l'état de connexion lorsqu'un client perd temporairement la connectivité ?
SignalR maintient l'état de connexion côté serveur pendant une période configurable. Avec la reconnexion automatique activée, le client tente de se reconnecter en utilisant le même identifiant de connexion. En cas de succès, la connexion reprend sans perdre l'appartenance aux groupes. Si la déconnexion dépasse le délai d'attente, une nouvelle connexion est établie et le client doit rejoindre à nouveau les groupes.
Q : Quelle est la différence entre Clients.User() et Clients.Client() ?
Clients.User(userId) cible toutes les connexions appartenant à un utilisateur authentifié spécifique—utile lorsqu'un utilisateur a plusieurs onglets de navigateur ouverts. Clients.Client(connectionId) cible une connexion spécifique unique. Le ciblage basé sur l'utilisateur nécessite l'authentification et utilise IUserIdProvider pour mapper les connexions aux identifiants utilisateur.
Q : Comment implémenter la détection de présence (afficher qui est en ligne) ?
Le suivi des connexions dans OnConnectedAsync et OnDisconnectedAsync est nécessaire. Les mappages utilisateur-vers-connexion doivent être stockés dans un dictionnaire concurrent ou un cache distribué. Pour les déploiements à montée en charge, un stockage partagé comme Redis est recommandé. Les mises à jour de présence doivent être diffusées aux groupes concernés lorsque les utilisateurs se connectent ou se déconnectent.
Q : Expliquez le but d'un backplane SignalR.
Un backplane synchronise les messages entre plusieurs instances de serveur dans un environnement à répartition de charge. Sans lui, un message envoyé depuis le serveur A n'atteint que les clients connectés au serveur A. Redis et Azure SignalR Service sont des options de backplane courantes. Le backplane publie les messages sur un canal partagé auquel tous les serveurs s'abonnent.
Stratégies d'Optimisation des Performances
Les performances de SignalR dépendent de la taille des messages, de leur fréquence et du nombre de connexions. La documentation des meilleures pratiques de performance ASP.NET Core fournit des benchmarks détaillés.
- Utiliser le protocole MessagePack pour la sérialisation binaire—réduit la taille de la charge utile de 30-50% par rapport à JSON
- Regrouper les messages quand c'est possible au lieu d'envoyer de nombreux petits messages
- Limiter la taille des groupes à quelques milliers de membres ; pour des audiences plus larges, considérer les patterns pub/sub
- Activer la compression pour les charges utiles riches en texte :
.AddJsonProtocol().AddHubOptions(o => o.EnableDetailedErrors = false) - Définir des délais d'attente appropriés pour libérer les ressources des connexions obsolètes rapidement
// MessagePack configuration for smaller payloads
builder.Services.AddSignalR()
.AddMessagePackProtocol(options =>
{
options.SerializerOptions =
MessagePackSerializerOptions.Standard
.WithCompression(MessagePackCompression.Lz4Block);
});Conclusion
- SignalR abstrait la sélection du transport (WebSockets, SSE, Long Polling) et fournit un modèle de programmation cohérent pour la communication temps réel
- Les Hubs centralisent la gestion des connexions ; les hubs fortement typés ajoutent la sécurité à la compilation pour les appels de méthodes clients
- Les groupes permettent une diffusion ciblée des messages à des sous-ensembles de connexions—rejoindre les groupes après reconnexion
- L'authentification s'intègre avec l'identité ASP.NET Core ; passer les tokens via la chaîne de requête pour les connexions WebSocket
- La montée en charge horizontale avec un backplane Redis ou Azure SignalR Service synchronise les messages entre les instances de serveur
- Le streaming supporte la livraison incrémentale de données avec
IAsyncEnumerablepour les scénarios serveur-vers-client et client-vers-serveur
Passe à la pratique !
Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.

Écrit par
Anthony Fillion-MailletDéveloppeur fullstack, fondateur de SharpSkill
Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.
Mis à jour le 12 août 2026
Tags
Partager
Articles similaires

Entity Framework Core : Optimisation des Performances et Bonnes Pratiques en 2026
Guide complet sur l'optimisation des performances Entity Framework Core 10 avec .NET 10. AsNoTracking, requêtes compilées, mises à jour par lot, split queries et LeftJoin.

.NET 10 en 2026 : Nouvelles Fonctionnalités, AOT Natif et Questions d'Entretien
Découvrez .NET 10 et C# 14 en 2026 : compilation AOT native en production, extension members, le mot-clé field, applications sans projet et questions d'entretien techniques.

.NET 9 Blazor : Développement Full-Stack avec Blazor United en 2026
.NET 9 Blazor United réunit le rendu statique SSR, le mode Server et WebAssembly dans un framework full-stack unifié. Un tutoriel pratique couvrant les modes de rendu, le streaming, l'injection par constructeur et les patterns de production.