SignalR no ASP.NET Core 2026: Tempo Real, Hubs e Perguntas de Entrevista
Guia completo de SignalR para aplicações web em tempo real com ASP.NET Core. Hubs, grupos, autenticação, escalabilidade com Redis e perguntas de entrevista técnica.

SignalR permite a comunicação bidirecional em tempo real entre servidores e clientes em aplicações ASP.NET Core. Diferentemente dos padrões tradicionais de requisição-resposta HTTP, o SignalR mantém conexões persistentes que permitem aos servidores enviar atualizações aos clientes instantaneamente—essencial para aplicações de chat, dashboards ao vivo, edição colaborativa e notificações em tempo real.
O SignalR seleciona automaticamente o melhor transporte disponível: primeiro WebSockets, depois Server-Sent Events, então Long Polling. Esse mecanismo de fallback garante compatibilidade com todos os navegadores e configurações de rede sem necessidade de alterações no código.
Como os Hubs do SignalR Habilitam a Comunicação Servidor-Cliente
Um Hub atua como um pipeline de alto nível que gerencia as invocações de métodos entre clientes e servidores. Os clientes chamam métodos no hub, e o hub pode invocar métodos nos clientes conectados. Essa abstração elimina a complexidade de gerenciar conexões WebSocket manualmente.
O exemplo a seguir demonstra um hub de chat básico que transmite mensagens para todos os clientes conectados:
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);
}
}A propriedade Clients fornece acesso a todos os clientes conectados através de várias opções de direcionamento: All, Caller, Others, Group e User.
Configurando o SignalR no ASP.NET Core 9
O registro do SignalR no ASP.NET Core 9 segue o padrão de middleware padrão. A configuração abaixo habilita os protocolos JSON e MessagePack com tamanhos de buffer personalizados:
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();As configurações KeepAliveInterval e ClientTimeoutInterval controlam o monitoramento da saúde das conexões. Clientes que não responderem dentro do período de timeout são considerados desconectados.
Gerenciando Grupos para Entrega Direcionada de Mensagens
Os grupos fornecem um mecanismo para organizar conexões e enviar mensagens para subconjuntos de clientes. Um caso de uso típico envolve salas de chat, onde os usuários entram em salas específicas e recebem apenas mensagens dessas salas.
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
);
}
}A associação a grupos está vinculada às conexões, não aos usuários. Quando um usuário se reconecta, ele precisa entrar novamente nos seus grupos. Para associação de grupo persistente, as associações de grupos devem ser armazenadas em um banco de dados e reconectadas automaticamente no OnConnectedAsync.
Os grupos existem apenas em memória e não são persistidos entre reinicializações do servidor ou em cenários de escalabilidade sem um backplane. Para implantações em produção com múltiplos servidores, o Redis ou o Azure SignalR Service deve ser usado como backplane.
Protegendo Conexões SignalR com Autenticação
O SignalR se integra com a autenticação do ASP.NET Core. O atributo [Authorize] restringe o acesso ao hub para usuários autenticados, e Context.User fornece acesso às claims do usuário autenticado.
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);
}
}Para conexões WebSocket, os tokens de autenticação devem ser passados via query string, pois WebSockets não suportam headers personalizados. A configuração do cliente JavaScript para incluir o token é a seguinte:
import * as signalR from "@microsoft/signalr";
const connection = new signalR.HubConnectionBuilder()
.withUrl("/securehub", {
accessTokenFactory: () => localStorage.getItem("authToken") || ""
})
.withAutomaticReconnect()
.build();
await connection.start();No servidor, a autenticação JWT deve ser configurada para ler o token da 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 para mandar bem nas entrevistas de .NET?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Escalando o SignalR com Backplane Redis
Em um ambiente com balanceamento de carga, os clientes podem se conectar a diferentes instâncias do servidor. Sem um backplane, as mensagens enviadas de um servidor só chegam aos clientes conectados àquele servidor. O Redis serve como backplane para sincronizar mensagens entre todas as instâncias do servidor.
builder.Services.AddSignalR()
.AddStackExchangeRedis(options =>
{
options.Configuration = builder.Configuration
.GetConnectionString("Redis");
// Optional: configure Redis-specific options
options.Configuration.ChannelPrefix =
RedisChannel.Literal("MyApp_SignalR_");
});O backplane Redis publica todas as mensagens do SignalR em canais Redis. Cada instância do servidor se inscreve nesses canais e entrega as mensagens aos seus clientes locais. Essa abordagem adiciona latência mínima (tipicamente 1-2ms) enquanto habilita a escalabilidade horizontal.
Para implantações empresariais, o Azure SignalR Service fornece uma alternativa gerenciada que lida com escalabilidade, gerenciamento de conexões e disponibilidade automaticamente.
Hubs Fortemente Tipados para Segurança em Tempo de Compilação
Os hubs fortemente tipados substituem strings mágicas por métodos de interface, habilitando a verificação em tempo de compilação das chamadas de métodos do cliente:
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);
}
}A refatoração se torna mais segura porque renomear um método na interface mostra imediatamente todos os locais de chamada que precisam de atualização.
Streaming de Dados com Canais SignalR
O SignalR suporta streaming para cenários onde os dados são produzidos incrementalmente, como atualizações de progresso, monitoramento de logs ou dados de sensores em tempo real. Tanto o streaming servidor-para-cliente quanto cliente-para-servidor são suportados.
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);O cliente JavaScript consome o stream usando um iterador assíncrono:
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")
});Perguntas de Entrevista Comuns sobre SignalR
As entrevistas técnicas frequentemente cobrem a arquitetura do SignalR e cenários do mundo real. As seguintes perguntas aparecem regularmente em sessões de entrevista .NET:
É importante explicar o processo de negociação de transporte: o SignalR tenta primeiro WebSockets, depois recorre a Server-Sent Events, então Long Polling. Compreender quando cada transporte é usado e suas limitações é fundamental.
P: Como o SignalR gerencia o estado da conexão quando um cliente perde conectividade temporariamente?
O SignalR mantém o estado da conexão no servidor por um período configurável. Com a reconexão automática habilitada, o cliente tenta se reconectar usando o mesmo ID de conexão. Se bem-sucedido, a conexão é retomada sem perder associações de grupo. Se a desconexão exceder o timeout, uma nova conexão é estabelecida e o cliente precisa entrar novamente nos grupos.
P: Qual é a diferença entre Clients.User() e Clients.Client()?
Clients.User(userId) direciona todas as conexões pertencentes a um usuário autenticado específico—útil quando um usuário tem múltiplas abas do navegador abertas. Clients.Client(connectionId) direciona uma única conexão específica. O direcionamento baseado em usuário requer autenticação e usa IUserIdProvider para mapear conexões para IDs de usuário.
P: Como implementar detecção de presença (mostrar quem está online)?
O rastreamento de conexões no OnConnectedAsync e OnDisconnectedAsync é necessário. Os mapeamentos de usuário para conexão devem ser armazenados em um dicionário concorrente ou cache distribuído. Para implantações escaláveis, recomenda-se usar um armazenamento compartilhado como Redis. As atualizações de presença devem ser transmitidas para os grupos relevantes quando os usuários se conectam ou desconectam.
P: Explique o propósito de um backplane do SignalR.
Um backplane sincroniza mensagens entre múltiplas instâncias de servidor em um ambiente com balanceamento de carga. Sem ele, uma mensagem enviada do servidor A só chega aos clientes conectados ao servidor A. Redis e Azure SignalR Service são opções comuns de backplane. O backplane publica mensagens em um canal compartilhado ao qual todos os servidores se inscrevem.
Estratégias de Otimização de Performance
A performance do SignalR depende do tamanho das mensagens, frequência e contagem de conexões. A documentação de melhores práticas de performance do ASP.NET Core fornece benchmarks detalhados.
- Usar protocolo MessagePack para serialização binária—reduz o tamanho do payload em 30-50% comparado ao JSON
- Agrupar mensagens quando possível em vez de enviar muitas mensagens pequenas
- Limitar tamanhos de grupo a milhares de membros; para audiências maiores, considerar padrões pub/sub
- Habilitar compressão para payloads com muito texto:
.AddJsonProtocol().AddHubOptions(o => o.EnableDetailedErrors = false) - Definir timeouts apropriados para liberar recursos de conexões obsoletas rapidamente
// MessagePack configuration for smaller payloads
builder.Services.AddSignalR()
.AddMessagePackProtocol(options =>
{
options.SerializerOptions =
MessagePackSerializerOptions.Standard
.WithCompression(MessagePackCompression.Lz4Block);
});Conclusão
- O SignalR abstrai a seleção de transporte (WebSockets, SSE, Long Polling) e fornece um modelo de programação consistente para comunicação em tempo real
- Os Hubs centralizam o gerenciamento de conexões; hubs fortemente tipados adicionam segurança em tempo de compilação para chamadas de métodos do cliente
- Os grupos habilitam entrega direcionada de mensagens para subconjuntos de conexões—entrar novamente nos grupos após reconexão
- A autenticação se integra com a identidade do ASP.NET Core; passar tokens via query string para conexões WebSocket
- Escalar horizontalmente com backplane Redis ou Azure SignalR Service para sincronizar mensagens entre instâncias de servidor
- O streaming suporta entrega incremental de dados com
IAsyncEnumerablepara cenários tanto servidor-para-cliente quanto cliente-para-servidor
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Escrito por
Anthony Fillion-MailletDesenvolvedor fullstack, fundador da SharpSkill
Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.
Atualizado em 12 de agosto de 2026
Tags
Compartilhar
Artigos relacionados

Entity Framework Core: Otimização de Performance e Boas Práticas em 2026
Guia completo de otimização de performance com Entity Framework Core 10 no .NET 10. AsNoTracking, queries compiladas, atualizações em lote, split queries e LeftJoin.

.NET 10 em 2026: Novos Recursos, Native AOT e Perguntas de Entrevista
Descubra os novos recursos do .NET 10 em 2026: Native AOT com binários de ~1MB, C# 14 com extension members e field keyword, ASP.NET Core 10 e perguntas de entrevista para desenvolvedores.

.NET 9 Blazor: Desenvolvimento Full-Stack com Blazor United em 2026
.NET 9 Blazor United combina renderização estática SSR, Server e WebAssembly em um framework full-stack unificado. Tutorial prático cobrindo modos de renderização, streaming, injeção por construtor e padrões prontos para produção.