SignalR in ASP.NET Core 2026: Real-Time Communicatie, Hubs en Sollicitatievragen

SignalR beheersen voor real-time webapplicaties in ASP.NET Core. Hubs, groepen, schalen met Redis en veelvoorkomende sollicitatievragen.

SignalR in ASP.NET Core 2026: Real-Time Communicatie, Hubs en Sollicitatievragen

SignalR maakt bidirectionele real-time communicatie mogelijk tussen servers en clients in ASP.NET Core-applicaties. In tegenstelling tot traditionele HTTP request-response patronen onderhoudt SignalR persistente verbindingen waarmee servers direct updates naar clients kunnen pushen, essentieel voor chatapplicaties, live dashboards, collaboratief bewerken en real-time notificaties.

SignalR Transport Fallback

SignalR selecteert automatisch het beste beschikbare transport: eerst WebSockets, dan Server-Sent Events, dan Long Polling. Dit fallback-mechanisme garandeert compatibiliteit over alle browsers en netwerkconfiguraties zonder codewijzigingen.

Hoe SignalR Hubs Server-Client Communicatie Mogelijk Maken

Een Hub fungeert als een high-level pipeline die method-aanroepen tussen clients en servers afhandelt. Clients roepen methodes aan op de hub, en de hub kan methodes aanroepen op verbonden clients. Deze abstractie elimineert de complexiteit van handmatig WebSocket-verbindingsbeheer.

Het volgende voorbeeld demonstreert een basis chat hub die berichten broadcast naar alle verbonden clients:

ChatHub.cscsharp
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);
    }
}

De Clients-property biedt toegang tot alle verbonden clients via verschillende targeting-opties: All, Caller, Others, Group en User.

SignalR Configureren in ASP.NET Core 9

SignalR-registratie in ASP.NET Core 9 volgt het standaard middleware-patroon. De onderstaande configuratie activeert JSON- en MessagePack-protocollen met aangepaste buffergroottes:

Program.cscsharp
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();

De KeepAliveInterval- en ClientTimeoutInterval-instellingen regelen de monitoring van de verbindingsstatus. Clients die niet binnen de timeout-periode reageren, worden als verbroken beschouwd.

Groepsbeheer voor Gerichte Berichtaflevering

Groepen bieden een mechanisme om verbindingen te organiseren en berichten naar subsets van clients te sturen. Een typisch gebruik betreft chatrooms, waar gebruikers specifieke kamers joinen en alleen berichten uit die kamers ontvangen.

GroupChatHub.cscsharp
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
        );
    }
}

Groepslidmaatschap is gebonden aan verbindingen, niet aan gebruikers. Wanneer een gebruiker opnieuw verbindt, moet deze opnieuw lid worden van de groepen. Voor persistent groepslidmaatschap moeten groepsassociaties worden opgeslagen in een database en automatisch worden hersteld in OnConnectedAsync.

Groepspersistentie

Groepen bestaan alleen in het geheugen en worden niet gepersisteerd over server-herstarts of in geschaalde scenario's zonder backplane. Voor productie-deployments met meerdere servers wordt Redis of Azure SignalR Service als backplane aanbevolen.

SignalR-Verbindingen Beveiligen met Authenticatie

SignalR integreert met ASP.NET Core-authenticatie. Het [Authorize]-attribuut beperkt hub-toegang tot geauthenticeerde gebruikers, en Context.User biedt toegang tot de claims van de geauthenticeerde gebruiker.

SecureHub.cscsharp
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);
    }
}

Voor WebSocket-verbindingen moeten authenticatietokens via query string worden doorgegeven, aangezien WebSockets geen aangepaste headers ondersteunen. De JavaScript-client wordt geconfigureerd om het token mee te sturen:

signalr-client.tstypescript
import * as signalR from "@microsoft/signalr";

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/securehub", {
        accessTokenFactory: () => localStorage.getItem("authToken") || ""
    })
    .withAutomaticReconnect()
    .build();

await connection.start();

Op de server wordt JWT-authenticatie geconfigureerd om het token uit de query string te lezen:

Program.cs - JWT configuration for SignalRcsharp
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;
            }
        };
    });

Klaar om je .NET gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

SignalR Schalen met Redis Backplane

In een load-balanced omgeving kunnen clients verbonden zijn met verschillende serverinstanties. Zonder backplane bereiken berichten die vanaf één server worden verzonden alleen de clients die met die server verbonden zijn. Redis dient als backplane om berichten te synchroniseren over alle serverinstanties.

Program.cs - Redis backplane configurationcsharp
builder.Services.AddSignalR()
    .AddStackExchangeRedis(options =>
    {
        options.Configuration = builder.Configuration
            .GetConnectionString("Redis");
        
        // Optional: configure Redis-specific options
        options.Configuration.ChannelPrefix = 
            RedisChannel.Literal("MyApp_SignalR_");
    });

De Redis-backplane publiceert alle SignalR-berichten naar Redis-kanalen. Elke serverinstantie abonneert zich op deze kanalen en levert berichten af aan zijn lokale clients. Deze aanpak voegt minimale latentie toe (typisch 1-2ms) terwijl het horizontale schaling mogelijk maakt.

Voor enterprise deployments biedt Azure SignalR Service een beheerd alternatief dat schaling, verbindingsbeheer en beschikbaarheid automatisch afhandelt.

Strongly-Typed Hubs voor Compile-Time Veiligheid

Strongly-typed hubs vervangen magic strings door interface-methodes, waardoor compile-time verificatie van client-method-aanroepen mogelijk wordt:

INotificationClient.cscsharp
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);
    }
}

Refactoring wordt veiliger omdat het hernoemen van een methode in de interface direct alle aanroeplocaties toont die updates nodig hebben.

Data Streamen met SignalR Channels

SignalR ondersteunt streaming voor scenario's waarin data incrementeel wordt geproduceerd, zoals voortgangsupdates, log tailing of real-time sensordata. Zowel server-naar-client als client-naar-server streaming worden ondersteund.

StreamingHub.cscsharp
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);

De JavaScript-client consumeert de stream met behulp van een asynchrone iterator:

streaming-client.tstypescript
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")
});

Veelgestelde SignalR Sollicitatievragen

Technische sollicitatiegesprekken behandelen vaak SignalR-architectuur en praktijkscenario's. De volgende vragen komen regelmatig voor in .NET sollicitatiesessies:

Sollicitatietip

Leg het transport-onderhandelingsproces uit: SignalR probeert eerst WebSockets, valt dan terug op Server-Sent Events, dan Long Polling. Begrijp wanneer elk transport wordt gebruikt en wat de beperkingen zijn.

V: Hoe gaat SignalR om met verbindingsstatus wanneer een client tijdelijk de verbinding verliest?

SignalR behoudt de verbindingsstatus op de server voor een configureerbare periode. Met automatische herverbinding ingeschakeld, probeert de client opnieuw te verbinden met dezelfde verbindings-ID. Bij succes wordt de verbinding hervat zonder groepslidmaatschappen te verliezen. Als de onderbreking de timeout overschrijdt, wordt een nieuwe verbinding opgezet en moet de client opnieuw lid worden van groepen.

V: Wat is het verschil tussen Clients.User() en Clients.Client()?

Clients.User(userId) richt zich op alle verbindingen van een specifieke geauthenticeerde gebruiker, handig wanneer een gebruiker meerdere browsertabs heeft geopend. Clients.Client(connectionId) richt zich op een enkele specifieke verbinding. Gebruiker-gebaseerde targeting vereist authenticatie en gebruikt IUserIdProvider om verbindingen aan gebruiker-ID's te koppelen.

V: Hoe zou men aanwezigheidsdetectie implementeren (tonen wie online is)?

Verbindingen worden gevolgd in OnConnectedAsync en OnDisconnectedAsync. Gebruiker-naar-verbinding mappings worden opgeslagen in een concurrent dictionary of gedistribueerde cache. Voor geschaalde deployments wordt een gedeelde opslag zoals Redis gebruikt. Aanwezigheidsupdates worden gebroadcast naar relevante groepen wanneer gebruikers verbinden of verbreken.

V: Leg het doel van een SignalR-backplane uit.

Een backplane synchroniseert berichten over meerdere serverinstanties in een load-balanced omgeving. Zonder backplane bereiken berichten die vanaf server A worden verzonden alleen clients die met server A verbonden zijn. Redis en Azure SignalR Service zijn veelvoorkomende backplane-opties. De backplane publiceert berichten naar een gedeeld kanaal waarop alle servers geabonneerd zijn.

Strategieen voor Prestatie-Optimalisatie

SignalR-prestaties hangen af van berichtgrootte, frequentie en aantal verbindingen. De ASP.NET Core prestatie best practices documentatie biedt gedetailleerde benchmarks.

  • Gebruik MessagePack-protocol voor binaire serialisatie, vermindert payload-grootte met 30-50% vergeleken met JSON
  • Bundel berichten waar mogelijk in plaats van veel kleine berichten te versturen
  • Beperk groepsgroottes tot duizenden leden; voor grotere doelgroepen, overweeg pub/sub-patronen
  • Schakel compressie in voor tekst-zware payloads
  • Stel passende timeouts in om resources van verouderde verbindingen snel vrij te geven
csharp
// MessagePack configuration for smaller payloads
builder.Services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        options.SerializerOptions = 
            MessagePackSerializerOptions.Standard
                .WithCompression(MessagePackCompression.Lz4Block);
    });

Conclusie

  • SignalR abstraheert transportselectie (WebSockets, SSE, Long Polling) en biedt een consistent programmeermodel voor real-time communicatie
  • Hubs centraliseren verbindingsbeheer; strongly-typed hubs voegen compile-time veiligheid toe voor client-method-aanroepen
  • Groepen maken gerichte berichtaflevering naar subsets van verbindingen mogelijk, opnieuw lid worden van groepen is vereist na herverbinding
  • Authenticatie integreert met ASP.NET Core Identity; tokens worden via query string doorgegeven voor WebSocket-verbindingen
  • Schaal horizontaal met Redis-backplane of Azure SignalR Service om berichten te synchroniseren over serverinstanties
  • Streaming ondersteunt incrementele data-aflevering met IAsyncEnumerable voor zowel server-naar-client als client-naar-server scenario's

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Anthony Fillion-Maillet

Geschreven door

Anthony Fillion-Maillet

Fullstack-ontwikkelaar, oprichter van SharpSkill

Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.

Bijgewerkt op 12 augustus 2026

Delen

Gerelateerde artikelen