Usar hubs no SignalR para ASP.NET Core
Por Rachel Appel e Kevin Griffin
A API SignalR Hubs permite que clientes conectados chamem métodos no servidor, facilitando a comunicação em tempo real. O servidor define métodos que são chamados pelo cliente e o cliente define métodos que são chamados pelo servidor. SignalR também possibilita a comunicação indireta cliente a cliente, sempre mediada pelo SignalR Hub, permitindo o envio de mensagens entre clientes individuais, grupos ou para todos os clientes conectados. O SignalR cuida de tudo o que é necessário para possibilitar a comunicação em tempo real entre cliente e servidor e entre servidor e cliente.
Configurar hubs do SignalR
Para registrar os serviços exigidos pelos hubs do SignalR, chame AddSignalR em Program.cs
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
Para configurar pontos de extremidade do SignalR, chame MapHub, também em Program.cs
:
app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");
app.Run();
Observação
Os assemblies do lado do servidor do ASP.NET Core SignalR agora são instalados com o SDK do .NET Core. Consulte os assemblies SignalR na estrutura compartilhada para obter mais informações.
Criar e usar hubs
Crie um hub declarando uma classe que herda de Hub. Adicione métodos public
à classe para torná-los chamáveis pelos clientes:
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
}
Observação
Os hubs são transitórios:
- Não armazene o estado em uma propriedade da classe de hub. Cada chamada de método de hub é executada em uma nova instância de hub.
- Não instancie um hub diretamente por meio de injeção de dependência. Para enviar mensagens para um cliente de outro lugar em seu aplicativo, use um
IHubContext
. - Use
await
ao chamar métodos assíncronos que dependem de o hub permanecer ativo. Por exemplo, um método comoClients.All.SendAsync(...)
pode falhar se for chamado semawait
e o método de hub for concluído antes queSendAsync
termine.
O objeto Context
A classe Hub inclui uma propriedade Context que contém as seguintes propriedades com informações sobre a conexão:
Propriedade | Descrição |
---|---|
ConnectionId | Obtém a ID exclusiva para a conexão, atribuída por SignalR. Há uma ID de conexão para cada conexão. |
UserIdentifier | Obtém o identificador de usuário. Por padrão, SignalR usa o ClaimTypes.NameIdentifier do ClaimsPrincipal associado à conexão como o identificador de usuário. |
User | Obtém o ClaimsPrincipal associado ao usuário atual. |
Items | Obtém uma coleção de chave/valor que pode ser usada para compartilhar dados dentro do escopo dessa conexão. Os dados podem ser armazenados nesta coleção e persistirão para a conexão em diferentes invocações de método de hub. |
Features | Obtém a coleção de recursos disponíveis na conexão. Por enquanto, essa coleção não é necessária na maioria dos cenários, portanto, ainda não está documentada em detalhes. |
ConnectionAborted | Obtém um CancellationToken que notifica quando a conexão é anulada. |
Hub.Context também contém os seguintes métodos:
Método | Descrição |
---|---|
GetHttpContext | Retorna o HttpContext para a conexão, ou null se a conexão não está associada a uma solicitação HTTP. Para conexões HTTP, use esse método para obter informações como cabeçalhos HTTP e cadeias de caracteres de consulta. |
Abort | Anula a conexão. |
O objeto Clients
A classe Hub inclui uma propriedade Clients que contém as seguintes propriedades para comunicação entre o servidor e o cliente:
Propriedade | Descrição |
---|---|
All | Chama um método em todos os clientes conectados |
Caller | Chama um método no cliente que invocou o método de hub |
Others | Chama um método em todos os clientes conectados, exceto o cliente que invocou o método |
Hub.Clients também contém os seguintes métodos:
Método | Descrição |
---|---|
AllExcept | Chama um método em todos os clientes conectados, exceto para as conexões especificadas |
Client | Chama um método em um cliente conectado específico |
Clients | Chama um método em clientes conectados específicos |
Group | Chama um método em todas as conexões no grupo especificado |
GroupExcept | Chama um método em todas as conexões no grupo especificado, exceto as conexões especificadas |
Groups | Chama um método em vários grupos de conexões |
OthersInGroup | Chama um método em um grupo de conexões, excluindo o cliente que invocou o método de hub |
User | Chama um método em todas as conexões associadas a um usuário específico |
Users | Chama um método em todas as conexões associadas aos usuários especificados |
Cada propriedade ou método nas tabelas anteriores retorna um objeto com um método SendAsync
. O método SendAsync
recebe o nome do método do cliente a ser chamado e quaisquer parâmetros.
O objeto retornado pelos métodos Client
e Caller
também contém um método InvokeAsync
, que pode ser usado para aguardar um resultado do cliente.
Enviar mensagens para clientes
Para fazer chamadas para clientes específicos, use as propriedades do objeto Clients
. No exemplo a seguir, existem três métodos de hub:
SendMessage
envia uma mensagem para todos os clientes conectados, usandoClients.All
.SendMessageToCaller
envia uma mensagem de volta para o chamador, usandoClients.Caller
.SendMessageToGroup
envia uma mensagem para todos os clientes no grupoSignalR Users
.
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.SendAsync("ReceiveMessage", user, message);
public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
Hubs fortemente tipados
Uma desvantagem de usar SendAsync
é que ele depende de uma cadeia de caracteres para especificar o método do cliente a ser chamado. Isso deixará o código aberto para erros de runtime se o nome do método estiver escrito incorretamente ou estiver ausente no cliente.
Uma alternativa ao uso do SendAsync
é tipar fortemente a classe Hub com Hub<T>. No exemplo a seguir, o método do cliente ChatHub
foi extraído para uma interface chamada IChatClient
:
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
Essa interface pode ser usada para refatorar o exemplo ChatHub
anterior para ser fortemente tipado:
public class StronglyTypedChatHub : Hub<IChatClient>
{
public async Task SendMessage(string user, string message)
=> await Clients.All.ReceiveMessage(user, message);
public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.ReceiveMessage(user, message);
public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}
Usar Hub<IChatClient>
habilita a verificação em tempo de compilação dos métodos do cliente. Isso impede problemas causados pelo uso de cadeias de caracteres, pois Hub<T>
só pode fornecer acesso aos métodos definidos na interface. Usar um Hub<T>
fortemente tipado desabilita a capacidade de usar SendAsync
.
Observação
O sufixo Async
não é removido dos nomes de método. A menos que um método de cliente seja definido com .on('MyMethodAsync')
, não use MyMethodAsync
como o nome.
Resultados do cliente
Além de fazer chamadas para clientes, o servidor pode solicitar um resultado de um cliente. Isso requer que o servidor use ISingleClientProxy.InvokeAsync
e o cliente retorne um resultado de seu manipulador .On
.
Há duas maneiras de usar a API no servidor, a primeira é chamar Client(...)
ou Caller
na propriedade Clients
em um método de hub:
public class ChatHub : Hub
{
public async Task<string> WaitForMessage(string connectionId)
{
var message = await Clients.Client(connectionId).InvokeAsync<string>(
"GetMessage");
return message;
}
}
A segunda maneira é chamar Client(...)
em uma instância do IHubContext<T>
:
async Task SomeMethod(IHubContext<MyHub> context)
{
string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
"GetMessage");
}
Hubs fortemente tipados também podem retornar valores de métodos de interface:
public interface IClient
{
Task<string> GetMessage();
}
public class ChatHub : Hub<IClient>
{
public async Task<string> WaitForMessage(string connectionId)
{
string message = await Clients.Client(connectionId).GetMessage();
return message;
}
}
Os clientes retornam resultados em seus manipuladores .On(...)
, conforme mostrado abaixo:
Cliente .NET
hubConnection.On("GetMessage", async () =>
{
Console.WriteLine("Enter message:");
var message = await Console.In.ReadLineAsync();
return message;
});
Cliente Typescript
hubConnection.on("GetMessage", async () => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("message");
}, 100);
});
return promise;
});
Cliente Java
hubConnection.onWithResult("GetMessage", () -> {
return Single.just("message");
});
Alterar o nome de um método de hub
Por padrão, um nome de método de hub do servidor é o nome do método .NET. Para alterar esse comportamento padrão para um método específico, use o atributo HubMethodName . O cliente deve usar esse nome em vez do nome do método .NET ao invocar o método:
[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
=> await Clients.User(user).SendAsync("ReceiveMessage", user, message);
Injetar serviços em um hub
Os construtores de hub podem aceitar serviços de DI como parâmetros, que podem ser armazenados em propriedades na classe para uso em um método de hub.
Ao injetar vários serviços em diferentes métodos de hub ou como uma maneira alternativa de programar, os métodos de hub também podem aceitar serviços da DI. Por padrão, os parâmetros do método de hub são inspecionados e resolvidos a partir da DI, se possível.
services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();
// ...
public class ChatHub : Hub
{
public Task SendMessage(string user, string message, IDatabaseService dbService)
{
var userName = dbService.GetUserName(user);
return Clients.All.SendAsync("ReceiveMessage", userName, message);
}
}
Se a resolução implícita de parâmetros de serviços não for desejada, desabilite-a com DisableImplicitFromServicesParameters.
Para especificar explicitamente quais parâmetros são resolvidos a partir da DI em métodos de hub, use a opção DisableImplicitFromServicesParameters
e use o atributo [FromServices]
ou um atributo personalizado que implementa IFromServiceMetadata
nos parâmetros de método de hub que devem ser resolvidos a partir da DI.
services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();
services.AddSignalR(options =>
{
options.DisableImplicitFromServicesParameters = true;
});
// ...
public class ChatHub : Hub
{
public Task SendMessage(string user, string message,
[FromServices] IDatabaseService dbService)
{
var userName = dbService.GetUserName(user);
return Clients.All.SendAsync("ReceiveMessage", userName, message);
}
}
Observação
Esse recurso usa IServiceProviderIsService, que opcionalmente é implementado por implementações da DI. Se o contêiner da DI do aplicativo não oferecer suporte a esse recurso, não há suporte para a injeção de serviços em métodos de hub.
Suporte a serviços com chave na Injeção de Dependência
Os serviços com chave referem-se a um mecanismo para registrar e recuperar serviços de Injeção de Dependência (DI) por meio de chaves. Um serviço é associado a uma chave chamando AddKeyedSingleton (ou AddKeyedScoped
ou AddKeyedTransient
) para registrá-la. Acesse um serviço registrado especificando a chave com o atributo [FromKeyedServices]
. O código a seguir mostra como usar serviços com chave:
using Microsoft.AspNetCore.SignalR;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
var app = builder.Build();
app.MapRazorPages();
app.MapHub<MyHub>("/myHub");
app.Run();
public interface ICache
{
object Get(string key);
}
public class BigCache : ICache
{
public object Get(string key) => $"Resolving {key} from big cache.";
}
public class SmallCache : ICache
{
public object Get(string key) => $"Resolving {key} from small cache.";
}
public class MyHub : Hub
{
public void SmallCacheMethod([FromKeyedServices("small")] ICache cache)
{
Console.WriteLine(cache.Get("signalr"));
}
public void BigCacheMethod([FromKeyedServices("big")] ICache cache)
{
Console.WriteLine(cache.Get("signalr"));
}
}
Manipular eventos para uma conexão
A API Hubs do SignalR fornece os métodos virtuais OnConnectedAsync e OnDisconnectedAsync para gerenciar e acompanhar conexões. Substitua o método virtual OnConnectedAsync
para executar ações quando um cliente se conectar ao hub, como adicioná-lo a um grupo:
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
Substitua o método virtual OnDisconnectedAsync
para executar ações quando um cliente se desconectar. Se o cliente se desconectar intencionalmente, como chamando connection.stop()
, o parâmetro exception
será definido como null
. No entanto, se o cliente se desconectar devido a um erro, como uma falha de rede, o parâmetro exception
conterá uma exceção que descreve a falha:
public override async Task OnDisconnectedAsync(Exception? exception)
{
await base.OnDisconnectedAsync(exception);
}
RemoveFromGroupAsync não precisa ser chamado no OnDisconnectedAsync, ele é tratado automaticamente para você.
Tratar erros
Exceções geradas em métodos de hub são enviadas ao cliente que invocou o método . No cliente JavaScript, o método invoke
retorna um JavaScript Promise
. Os clientes podem anexar um manipulador catch
à promessa retornada ou usar try
/catch
com async
/await
para lidar com exceções:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
As conexões não são fechadas quando um hub lança uma exceção. Por padrão, SignalR retorna uma mensagem de erro genérica para o cliente, conforme mostrado no exemplo a seguir:
Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' on the server.
Exceções inesperadas geralmente contêm informações confidenciais, como o nome de um servidor de banco de dados em uma exceção disparada quando a conexão de banco de dados falha. SignalR não expõe essas mensagens de erro detalhadas por padrão como uma medida de segurança. Para obter mais informações sobre por que os detalhes da exceção são suprimidos, confira as Considerações de segurança no ASP.NET Core SignalR.
Se uma condição excepcional precisar ser propagada para o cliente, use a classe HubException. Se um HubException
for lançado em um método de hub, o SignalRenviará toda a mensagem de exceção para o cliente, sem modificação:
public Task ThrowException()
=> throw new HubException("This error will be sent to the client!");
Observação
SignalR apenas envia a propriedade Message
da exceção para o cliente. O rastreamento de pilha e outras propriedades na exceção não estão disponíveis para o cliente.
Recursos adicionais
Por Rachel Appel e Kevin Griffin
A API SignalR Hubs permite que clientes conectados chamem métodos no servidor, facilitando a comunicação em tempo real. O servidor define métodos que são chamados pelo cliente e o cliente define métodos que são chamados pelo servidor. SignalR também possibilita a comunicação indireta cliente a cliente, sempre mediada pelo SignalR Hub, permitindo o envio de mensagens entre clientes individuais, grupos ou para todos os clientes conectados. O SignalR cuida de tudo o que é necessário para possibilitar a comunicação em tempo real entre cliente e servidor e entre servidor e cliente.
Configurar hubs do SignalR
Para registrar os serviços exigidos pelos hubs do SignalR, chame AddSignalR em Program.cs
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
Para configurar pontos de extremidade do SignalR, chame MapHub, também em Program.cs
:
app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");
app.Run();
Observação
Os assemblies do lado do servidor do ASP.NET Core SignalR agora são instalados com o SDK do .NET Core. Consulte os assemblies SignalR na estrutura compartilhada para obter mais informações.
Criar e usar hubs
Crie um hub declarando uma classe que herda de Hub. Adicione métodos public
à classe para torná-los chamáveis pelos clientes:
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
}
Observação
Os hubs são transitórios:
- Não armazene o estado em uma propriedade da classe de hub. Cada chamada de método de hub é executada em uma nova instância de hub.
- Não instancie um hub diretamente por meio de injeção de dependência. Para enviar mensagens para um cliente de outro lugar em seu aplicativo, use um
IHubContext
. - Use
await
ao chamar métodos assíncronos que dependem de o hub permanecer ativo. Por exemplo, um método comoClients.All.SendAsync(...)
pode falhar se for chamado semawait
e o método de hub for concluído antes queSendAsync
termine.
O objeto Context
A classe Hub inclui uma propriedade Context que contém as seguintes propriedades com informações sobre a conexão:
Propriedade | Descrição |
---|---|
ConnectionId | Obtém a ID exclusiva para a conexão, atribuída por SignalR. Há uma ID de conexão para cada conexão. |
UserIdentifier | Obtém o identificador de usuário. Por padrão, SignalR usa o ClaimTypes.NameIdentifier do ClaimsPrincipal associado à conexão como o identificador de usuário. |
User | Obtém o ClaimsPrincipal associado ao usuário atual. |
Items | Obtém uma coleção de chave/valor que pode ser usada para compartilhar dados dentro do escopo dessa conexão. Os dados podem ser armazenados nesta coleção e persistirão para a conexão em diferentes invocações de método de hub. |
Features | Obtém a coleção de recursos disponíveis na conexão. Por enquanto, essa coleção não é necessária na maioria dos cenários, portanto, ainda não está documentada em detalhes. |
ConnectionAborted | Obtém um CancellationToken que notifica quando a conexão é anulada. |
Hub.Context também contém os seguintes métodos:
Método | Descrição |
---|---|
GetHttpContext | Retorna o HttpContext para a conexão, ou null se a conexão não está associada a uma solicitação HTTP. Para conexões HTTP, use esse método para obter informações como cabeçalhos HTTP e cadeias de caracteres de consulta. |
Abort | Anula a conexão. |
O objeto Clients
A classe Hub inclui uma propriedade Clients que contém as seguintes propriedades para comunicação entre o servidor e o cliente:
Propriedade | Descrição |
---|---|
All | Chama um método em todos os clientes conectados |
Caller | Chama um método no cliente que invocou o método de hub |
Others | Chama um método em todos os clientes conectados, exceto o cliente que invocou o método |
Hub.Clients também contém os seguintes métodos:
Método | Descrição |
---|---|
AllExcept | Chama um método em todos os clientes conectados, exceto para as conexões especificadas |
Client | Chama um método em um cliente conectado específico |
Clients | Chama um método em clientes conectados específicos |
Group | Chama um método em todas as conexões no grupo especificado |
GroupExcept | Chama um método em todas as conexões no grupo especificado, exceto as conexões especificadas |
Groups | Chama um método em vários grupos de conexões |
OthersInGroup | Chama um método em um grupo de conexões, excluindo o cliente que invocou o método de hub |
User | Chama um método em todas as conexões associadas a um usuário específico |
Users | Chama um método em todas as conexões associadas aos usuários especificados |
Cada propriedade ou método nas tabelas anteriores retorna um objeto com um método SendAsync
. O método SendAsync
recebe o nome do método do cliente a ser chamado e quaisquer parâmetros.
O objeto retornado pelos métodos Client
e Caller
também contém um método InvokeAsync
, que pode ser usado para aguardar um resultado do cliente.
Enviar mensagens para clientes
Para fazer chamadas para clientes específicos, use as propriedades do objeto Clients
. No exemplo a seguir, existem três métodos de hub:
SendMessage
envia uma mensagem para todos os clientes conectados, usandoClients.All
.SendMessageToCaller
envia uma mensagem de volta para o chamador, usandoClients.Caller
.SendMessageToGroup
envia uma mensagem para todos os clientes no grupoSignalR Users
.
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.SendAsync("ReceiveMessage", user, message);
public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
Hubs fortemente tipados
Uma desvantagem de usar SendAsync
é que ele depende de uma cadeia de caracteres para especificar o método do cliente a ser chamado. Isso deixará o código aberto para erros de runtime se o nome do método estiver escrito incorretamente ou estiver ausente no cliente.
Uma alternativa ao uso do SendAsync
é tipar fortemente a classe Hub com Hub<T>. No exemplo a seguir, o método do cliente ChatHub
foi extraído para uma interface chamada IChatClient
:
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
Essa interface pode ser usada para refatorar o exemplo ChatHub
anterior para ser fortemente tipado:
public class StronglyTypedChatHub : Hub<IChatClient>
{
public async Task SendMessage(string user, string message)
=> await Clients.All.ReceiveMessage(user, message);
public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.ReceiveMessage(user, message);
public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}
Usar Hub<IChatClient>
habilita a verificação em tempo de compilação dos métodos do cliente. Isso impede problemas causados pelo uso de cadeias de caracteres, pois Hub<T>
só pode fornecer acesso aos métodos definidos na interface. Usar um Hub<T>
fortemente tipado desabilita a capacidade de usar SendAsync
.
Observação
O sufixo Async
não é removido dos nomes de método. A menos que um método de cliente seja definido com .on('MyMethodAsync')
, não use MyMethodAsync
como o nome.
Resultados do cliente
Além de fazer chamadas para clientes, o servidor pode solicitar um resultado de um cliente. Isso requer que o servidor use ISingleClientProxy.InvokeAsync
e o cliente retorne um resultado de seu manipulador .On
.
Há duas maneiras de usar a API no servidor, a primeira é chamar Client(...)
ou Caller
na propriedade Clients
em um método de hub:
public class ChatHub : Hub
{
public async Task<string> WaitForMessage(string connectionId)
{
var message = await Clients.Client(connectionId).InvokeAsync<string>(
"GetMessage");
return message;
}
}
A segunda maneira é chamar Client(...)
em uma instância do IHubContext<T>
:
async Task SomeMethod(IHubContext<MyHub> context)
{
string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
"GetMessage");
}
Hubs fortemente tipados também podem retornar valores de métodos de interface:
public interface IClient
{
Task<string> GetMessage();
}
public class ChatHub : Hub<IClient>
{
public async Task<string> WaitForMessage(string connectionId)
{
string message = await Clients.Client(connectionId).GetMessage();
return message;
}
}
Os clientes retornam resultados em seus manipuladores .On(...)
, conforme mostrado abaixo:
Cliente .NET
hubConnection.On("GetMessage", async () =>
{
Console.WriteLine("Enter message:");
var message = await Console.In.ReadLineAsync();
return message;
});
Cliente Typescript
hubConnection.on("GetMessage", async () => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("message");
}, 100);
});
return promise;
});
Cliente Java
hubConnection.onWithResult("GetMessage", () -> {
return Single.just("message");
});
Alterar o nome de um método de hub
Por padrão, um nome de método de hub do servidor é o nome do método .NET. Para alterar esse comportamento padrão para um método específico, use o atributo HubMethodName . O cliente deve usar esse nome em vez do nome do método .NET ao invocar o método:
[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
=> await Clients.User(user).SendAsync("ReceiveMessage", user, message);
Injetar serviços em um hub
Os construtores de hub podem aceitar serviços de DI como parâmetros, que podem ser armazenados em propriedades na classe para uso em um método de hub.
Ao injetar vários serviços em diferentes métodos de hub ou como uma maneira alternativa de programar, os métodos de hub também podem aceitar serviços da DI. Por padrão, os parâmetros do método de hub são inspecionados e resolvidos a partir da DI, se possível.
services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();
// ...
public class ChatHub : Hub
{
public Task SendMessage(string user, string message, IDatabaseService dbService)
{
var userName = dbService.GetUserName(user);
return Clients.All.SendAsync("ReceiveMessage", userName, message);
}
}
Se a resolução implícita de parâmetros de serviços não for desejada, desabilite-a com DisableImplicitFromServicesParameters.
Para especificar explicitamente quais parâmetros são resolvidos a partir da DI em métodos de hub, use a opção DisableImplicitFromServicesParameters
e use o atributo [FromServices]
ou um atributo personalizado que implementa IFromServiceMetadata
nos parâmetros de método de hub que devem ser resolvidos a partir da DI.
services.AddSingleton<IDatabaseService, DatabaseServiceImpl>();
services.AddSignalR(options =>
{
options.DisableImplicitFromServicesParameters = true;
});
// ...
public class ChatHub : Hub
{
public Task SendMessage(string user, string message,
[FromServices] IDatabaseService dbService)
{
var userName = dbService.GetUserName(user);
return Clients.All.SendAsync("ReceiveMessage", userName, message);
}
}
Observação
Esse recurso usa IServiceProviderIsService, que opcionalmente é implementado por implementações da DI. Se o contêiner da DI do aplicativo não oferecer suporte a esse recurso, não há suporte para a injeção de serviços em métodos de hub.
Manipular eventos para uma conexão
A API Hubs do SignalR fornece os métodos virtuais OnConnectedAsync e OnDisconnectedAsync para gerenciar e acompanhar conexões. Substitua o método virtual OnConnectedAsync
para executar ações quando um cliente se conectar ao hub, como adicioná-lo a um grupo:
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
Substitua o método virtual OnDisconnectedAsync
para executar ações quando um cliente se desconectar. Se o cliente se desconectar intencionalmente, como chamando connection.stop()
, o parâmetro exception
será definido como null
. No entanto, se o cliente se desconectar devido a um erro, como uma falha de rede, o parâmetro exception
conterá uma exceção que descreve a falha:
public override async Task OnDisconnectedAsync(Exception? exception)
{
await base.OnDisconnectedAsync(exception);
}
RemoveFromGroupAsync não precisa ser chamado no OnDisconnectedAsync, ele é tratado automaticamente para você.
Tratar erros
Exceções geradas em métodos de hub são enviadas ao cliente que invocou o método . No cliente JavaScript, o método invoke
retorna um JavaScript Promise
. Os clientes podem anexar um manipulador catch
à promessa retornada ou usar try
/catch
com async
/await
para lidar com exceções:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
As conexões não são fechadas quando um hub lança uma exceção. Por padrão, SignalR retorna uma mensagem de erro genérica para o cliente, conforme mostrado no exemplo a seguir:
Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' on the server.
Exceções inesperadas geralmente contêm informações confidenciais, como o nome de um servidor de banco de dados em uma exceção disparada quando a conexão de banco de dados falha. SignalR não expõe essas mensagens de erro detalhadas por padrão como uma medida de segurança. Para obter mais informações sobre por que os detalhes da exceção são suprimidos, confira as Considerações de segurança no ASP.NET Core SignalR.
Se uma condição excepcional precisar ser propagada para o cliente, use a classe HubException. Se um HubException
for lançado em um método de hub, o SignalRenviará toda a mensagem de exceção para o cliente, sem modificação:
public Task ThrowException()
=> throw new HubException("This error will be sent to the client!");
Observação
SignalR apenas envia a propriedade Message
da exceção para o cliente. O rastreamento de pilha e outras propriedades na exceção não estão disponíveis para o cliente.
Recursos adicionais
Por Rachel Appel e Kevin Griffin
A API SignalR Hubs permite que clientes conectados chamem métodos no servidor, facilitando a comunicação em tempo real. O servidor define métodos que são chamados pelo cliente e o cliente define métodos que são chamados pelo servidor. SignalR também possibilita a comunicação indireta cliente a cliente, sempre mediada pelo SignalR Hub, permitindo o envio de mensagens entre clientes individuais, grupos ou para todos os clientes conectados. O SignalR cuida de tudo o que é necessário para possibilitar a comunicação em tempo real entre cliente e servidor e entre servidor e cliente.
Configurar hubs do SignalR
Para registrar os serviços exigidos pelos hubs do SignalR, chame AddSignalR em Program.cs
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
Para configurar pontos de extremidade do SignalR, chame MapHub, também em Program.cs
:
app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");
app.Run();
Observação
Os assemblies do lado do servidor do ASP.NET Core SignalR agora são instalados com o SDK do .NET Core. Consulte os assemblies SignalR na estrutura compartilhada para obter mais informações.
Criar e usar hubs
Crie um hub declarando uma classe que herda de Hub. Adicione métodos public
à classe para torná-los chamáveis pelos clientes:
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
}
Observação
Os hubs são transitórios:
- Não armazene o estado em uma propriedade da classe de hub. Cada chamada de método de hub é executada em uma nova instância de hub.
- Não instancie um hub diretamente por meio de injeção de dependência. Para enviar mensagens para um cliente de outro lugar em seu aplicativo, use um
IHubContext
. - Use
await
ao chamar métodos assíncronos que dependem de o hub permanecer ativo. Por exemplo, um método comoClients.All.SendAsync(...)
pode falhar se for chamado semawait
e o método de hub for concluído antes queSendAsync
termine.
O objeto Context
A classe Hub inclui uma propriedade Context que contém as seguintes propriedades com informações sobre a conexão:
Propriedade | Descrição |
---|---|
ConnectionId | Obtém a ID exclusiva para a conexão, atribuída por SignalR. Há uma ID de conexão para cada conexão. |
UserIdentifier | Obtém o identificador de usuário. Por padrão, SignalR usa o ClaimTypes.NameIdentifier do ClaimsPrincipal associado à conexão como o identificador de usuário. |
User | Obtém o ClaimsPrincipal associado ao usuário atual. |
Items | Obtém uma coleção de chave/valor que pode ser usada para compartilhar dados dentro do escopo dessa conexão. Os dados podem ser armazenados nesta coleção e persistirão para a conexão em diferentes invocações de método de hub. |
Features | Obtém a coleção de recursos disponíveis na conexão. Por enquanto, essa coleção não é necessária na maioria dos cenários, portanto, ainda não está documentada em detalhes. |
ConnectionAborted | Obtém um CancellationToken que notifica quando a conexão é anulada. |
Hub.Context também contém os seguintes métodos:
Método | Descrição |
---|---|
GetHttpContext | Retorna o HttpContext para a conexão, ou null se a conexão não está associada a uma solicitação HTTP. Para conexões HTTP, use esse método para obter informações como cabeçalhos HTTP e cadeias de caracteres de consulta. |
Abort | Anula a conexão. |
O objeto Clients
A classe Hub inclui uma propriedade Clients que contém as seguintes propriedades para comunicação entre o servidor e o cliente:
Propriedade | Descrição |
---|---|
All | Chama um método em todos os clientes conectados |
Caller | Chama um método no cliente que invocou o método de hub |
Others | Chama um método em todos os clientes conectados, exceto o cliente que invocou o método |
Hub.Clients também contém os seguintes métodos:
Método | Descrição |
---|---|
AllExcept | Chama um método em todos os clientes conectados, exceto para as conexões especificadas |
Client | Chama um método em um cliente conectado específico |
Clients | Chama um método em clientes conectados específicos |
Group | Chama um método em todas as conexões no grupo especificado |
GroupExcept | Chama um método em todas as conexões no grupo especificado, exceto as conexões especificadas |
Groups | Chama um método em vários grupos de conexões |
OthersInGroup | Chama um método em um grupo de conexões, excluindo o cliente que invocou o método de hub |
User | Chama um método em todas as conexões associadas a um usuário específico |
Users | Chama um método em todas as conexões associadas aos usuários especificados |
Cada propriedade ou método nas tabelas anteriores retorna um objeto com um método SendAsync
. O método SendAsync
recebe o nome do método do cliente a ser chamado e quaisquer parâmetros.
Enviar mensagens para clientes
Para fazer chamadas para clientes específicos, use as propriedades do objeto Clients
. No exemplo a seguir, existem três métodos de hub:
SendMessage
envia uma mensagem para todos os clientes conectados, usandoClients.All
.SendMessageToCaller
envia uma mensagem de volta para o chamador, usandoClients.Caller
.SendMessageToGroup
envia uma mensagem para todos os clientes no grupoSignalR Users
.
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.SendAsync("ReceiveMessage", user, message);
public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
Hubs fortemente tipados
Uma desvantagem de usar SendAsync
é que ele depende de uma cadeia de caracteres para especificar o método do cliente a ser chamado. Isso deixará o código aberto para erros de runtime se o nome do método estiver escrito incorretamente ou estiver ausente no cliente.
Uma alternativa ao uso do SendAsync
é tipar fortemente a classe Hub com Hub<T>. No exemplo a seguir, o método do cliente ChatHub
foi extraído para uma interface chamada IChatClient
:
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
Essa interface pode ser usada para refatorar o exemplo ChatHub
anterior para ser fortemente tipado:
public class StronglyTypedChatHub : Hub<IChatClient>
{
public async Task SendMessage(string user, string message)
=> await Clients.All.ReceiveMessage(user, message);
public async Task SendMessageToCaller(string user, string message)
=> await Clients.Caller.ReceiveMessage(user, message);
public async Task SendMessageToGroup(string user, string message)
=> await Clients.Group("SignalR Users").ReceiveMessage(user, message);
}
Usar Hub<IChatClient>
habilita a verificação em tempo de compilação dos métodos do cliente. Isso impede problemas causados pelo uso de cadeias de caracteres, pois Hub<T>
só pode fornecer acesso aos métodos definidos na interface. Usar um Hub<T>
fortemente tipado desabilita a capacidade de usar SendAsync
.
Observação
O sufixo Async
não é removido dos nomes de método. A menos que um método de cliente seja definido com .on('MyMethodAsync')
, não use MyMethodAsync
como o nome.
Alterar o nome de um método de hub
Por padrão, um nome de método de hub do servidor é o nome do método .NET. Para alterar esse comportamento padrão para um método específico, use o atributo HubMethodName . O cliente deve usar esse nome em vez do nome do método .NET ao invocar o método:
[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
=> await Clients.User(user).SendAsync("ReceiveMessage", user, message);
Manipular eventos para uma conexão
A API Hubs do SignalR fornece os métodos virtuais OnConnectedAsync e OnDisconnectedAsync para gerenciar e acompanhar conexões. Substitua o método virtual OnConnectedAsync
para executar ações quando um cliente se conectar ao hub, como adicioná-lo a um grupo:
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
Substitua o método virtual OnDisconnectedAsync
para executar ações quando um cliente se desconectar. Se o cliente se desconectar intencionalmente, como chamando connection.stop()
, o parâmetro exception
será definido como null
. No entanto, se o cliente se desconectar devido a um erro, como uma falha de rede, o parâmetro exception
conterá uma exceção que descreve a falha:
public override async Task OnDisconnectedAsync(Exception? exception)
{
await base.OnDisconnectedAsync(exception);
}
RemoveFromGroupAsync não precisa ser chamado no OnDisconnectedAsync, ele é tratado automaticamente para você.
Tratar erros
Exceções geradas em métodos de hub são enviadas ao cliente que invocou o método . No cliente JavaScript, o método invoke
retorna um JavaScript Promise
. Os clientes podem anexar um manipulador catch
à promessa retornada ou usar try
/catch
com async
/await
para lidar com exceções:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
As conexões não são fechadas quando um hub lança uma exceção. Por padrão, SignalR retorna uma mensagem de erro genérica para o cliente, conforme mostrado no exemplo a seguir:
Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' on the server.
Exceções inesperadas geralmente contêm informações confidenciais, como o nome de um servidor de banco de dados em uma exceção disparada quando a conexão de banco de dados falha. SignalR não expõe essas mensagens de erro detalhadas por padrão como uma medida de segurança. Para obter mais informações sobre por que os detalhes da exceção são suprimidos, confira as Considerações de segurança no ASP.NET Core SignalR.
Se uma condição excepcional precisar ser propagada para o cliente, use a classe HubException. Se um HubException
for lançado em um método de hub, o SignalRenviará toda a mensagem de exceção para o cliente, sem modificação:
public Task ThrowException()
=> throw new HubException("This error will be sent to the client!");
Observação
SignalR apenas envia a propriedade Message
da exceção para o cliente. O rastreamento de pilha e outras propriedades na exceção não estão disponíveis para o cliente.
Recursos adicionais
Por Rachel Appel e Kevin Griffin
Exibir ou baixar código de exemplo (como baixar)
O que é um hub do SignalR
A API SignalR Hubs permite que clientes conectados chamem métodos no servidor, facilitando a comunicação em tempo real. O servidor define métodos que são chamados pelo cliente e o cliente define métodos que são chamados pelo servidor. SignalR também possibilita a comunicação indireta cliente a cliente, sempre mediada pelo SignalR Hub, permitindo o envio de mensagens entre clientes individuais, grupos ou para todos os clientes conectados. O SignalR cuida de tudo o que é necessário para possibilitar a comunicação em tempo real entre cliente e servidor e entre servidor e cliente.
Configurar hubs do SignalR
O middleware SignalR requer alguns serviços, que são configurados chamando AddSignalR:
services.AddSignalR();
Ao adicionar a funcionalidade SignalR a um aplicativo ASP.NET Core, configure rotas SignalR chamando MapHub na chamada de retorno UseEndpoints do método Startup.Configure
:
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/chathub");
});
Observação
Os assemblies do lado do servidor do ASP.NET Core SignalR agora são instalados com o SDK do .NET Core. Consulte os assemblies SignalR na estrutura compartilhada para obter mais informações.
Criar e usar hubs
Crie um hub declarando uma classe que herda de Hub e adicione métodos públicos a ele. Os clientes podem chamar métodos definidos como public
:
public class ChatHub : Hub
{
public Task SendMessage(string user, string message)
{
return Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
Você pode especificar um tipo de retorno e parâmetros, incluindo tipos complexos e matrizes, como faria em qualquer método C#. SignalR manipula a serialização e desserialização de objetos complexos e matrizes em seus parâmetros e valores retornados.
Observação
Os hubs são transitórios:
- Não armazene o estado em uma propriedade na classe de hub. Toda chamada de método de hub é executada em uma nova instância de hub.
- Não instancie um hub diretamente por meio de injeção de dependência. Para enviar mensagens para um cliente de outro lugar em seu aplicativo, use um
IHubContext
. - Use
await
ao chamar métodos assíncronos que dependem de o hub permanecer ativo. Por exemplo, um método comoClients.All.SendAsync(...)
pode falhar se for chamado semawait
e o método de hub for concluído antes queSendAsync
termine.
O objeto Context
A classe Hub tem uma propriedade Context que contém as seguintes propriedades com informações sobre a conexão:
Propriedade | Descrição |
---|---|
ConnectionId | Obtém a ID exclusiva para a conexão, atribuída por SignalR. Há uma ID de conexão para cada conexão. |
UserIdentifier | Obtém o identificador de usuário. Por padrão, SignalR usa o ClaimTypes.NameIdentifier do ClaimsPrincipal associado à conexão como o identificador de usuário. |
User | Obtém o ClaimsPrincipal associado ao usuário atual. |
Items | Obtém uma coleção de chave/valor que pode ser usada para compartilhar dados dentro do escopo dessa conexão. Os dados podem ser armazenados nesta coleção e persistirão para a conexão em diferentes invocações de método de hub. |
Features | Obtém a coleção de recursos disponíveis na conexão. Por enquanto, essa coleção não é necessária na maioria dos cenários, portanto, ainda não está documentada em detalhes. |
ConnectionAborted | Obtém um CancellationToken que notifica quando a conexão é anulada. |
Hub.Context também contém os seguintes métodos:
Método | Descrição |
---|---|
GetHttpContext | Retorna o HttpContext para a conexão, ou null se a conexão não está associada a uma solicitação HTTP. Para conexões HTTP, você pode usar esse método para obter informações como cabeçalhos HTTP e cadeias de caracteres de consulta. |
Abort | Anula a conexão. |
O objeto Clients
A classe Hub tem uma propriedade Clients que contém as seguintes propriedades para comunicação entre o servidor e o cliente:
Propriedade | Descrição |
---|---|
All | Chama um método em todos os clientes conectados |
Caller | Chama um método no cliente que invocou o método de hub |
Others | Chama um método em todos os clientes conectados, exceto o cliente que invocou o método |
Hub.Clients também contém os seguintes métodos:
Método | Descrição |
---|---|
AllExcept | Chama um método em todos os clientes conectados, exceto para as conexões especificadas |
Client | Chama um método em um cliente conectado específico |
Clients | Chama um método em clientes conectados específicos |
Group | Chama um método em todas as conexões no grupo especificado |
GroupExcept | Chama um método em todas as conexões no grupo especificado, exceto as conexões especificadas |
Groups | Chama um método em vários grupos de conexões |
OthersInGroup | Chama um método em um grupo de conexões, excluindo o cliente que invocou o método de hub |
User | Chama um método em todas as conexões associadas a um usuário específico |
Users | Chama um método em todas as conexões associadas aos usuários especificados |
Cada propriedade ou método nas tabelas anteriores retorna um objeto com um método SendAsync
. O método SendAsync
permite que você forneça o nome e os parâmetros do método do cliente a ser chamado.
Enviar mensagens para clientes
Para fazer chamadas para clientes específicos, use as propriedades do objeto Clients
. No exemplo a seguir, existem três Métodos de hub:
SendMessage
envia uma mensagem para todos os clientes conectados, usandoClients.All
.SendMessageToCaller
envia uma mensagem de volta para o chamador, usandoClients.Caller
.SendMessageToGroup
envia uma mensagem para todos os clientes no grupoSignalR Users
.
public Task SendMessage(string user, string message)
{
return Clients.All.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToCaller(string user, string message)
{
return Clients.Caller.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToGroup(string user, string message)
{
return Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
}
Hubs fortemente tipados
Uma desvantagem de usar SendAsync
é que ele depende de uma cadeia de caracteres mágica para especificar o método do cliente a ser chamado. Isso deixará o código aberto para erros de runtime se o nome do método estiver escrito incorretamente ou estiver ausente no cliente.
Uma alternativa ao uso do SendAsync
é tipar fortemente a Hub com Hub<T>. No exemplo a seguir, os métodos do cliente ChatHub
foram extraídos para uma interface chamada IChatClient
.
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
Essa interface pode ser usada para refatorar o exemplo ChatHub
anterior:
public class StronglyTypedChatHub : Hub<IChatClient>
{
public async Task SendMessage(string user, string message)
{
await Clients.All.ReceiveMessage(user, message);
}
public Task SendMessageToCaller(string user, string message)
{
return Clients.Caller.ReceiveMessage(user, message);
}
}
Usar Hub<IChatClient>
habilita a verificação em tempo de compilação dos métodos do cliente. Isso impede problemas causados pelo uso de cadeias de caracteres mágicas, pois Hub<T>
só pode fornecer acesso aos métodos definidos na interface.
Usar um Hub<T>
fortemente tipado desabilita a capacidade de usar SendAsync
. Todos os métodos definidos na interface ainda podem ser definidos como assíncronos. Na verdade, cada um desses métodos deve retornar um Task
. Como é uma interface, não use a palavra-chave async
. Por exemplo:
public interface IClient
{
Task ClientMethod();
}
Observação
O sufixo Async
não é removido do nome do método. A menos que o método do cliente seja definido com .on('MyMethodAsync')
, você não deve usar MyMethodAsync
como um nome.
Alterar o nome de um método de hub
Por padrão, um nome de método de hub do servidor é o nome do método .NET. No entanto, você pode usar o atributo HubMethodName para alterar esse padrão e especificar manualmente um nome para o método. O cliente deve usar esse nome, em vez do nome do método .NET, ao invocar o método :
[HubMethodName("SendMessageToUser")]
public Task DirectMessage(string user, string message)
{
return Clients.User(user).SendAsync("ReceiveMessage", user, message);
}
Manipular eventos para uma conexão
A API Hubs do SignalR fornece os métodos virtuais OnConnectedAsync e OnDisconnectedAsync para gerenciar e acompanhar conexões. Substitua o método virtual OnConnectedAsync
para executar ações quando um cliente se conectar ao Hub, como adicioná-lo a um grupo:
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
Substitua o método virtual OnDisconnectedAsync
para executar ações quando um cliente se desconectar. Se o cliente se desconectar intencionalmente (chamando connection.stop()
, por exemplo), o parâmetro exception
será null
. No entanto, se o cliente estiver desconectado devido a um erro (como uma falha de rede), o parâmetro exception
conterá uma exceção que descreve a falha:
public override async Task OnDisconnectedAsync(Exception exception)
{
await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", "I", "disconnect");
await base.OnDisconnectedAsync(exception);
}
RemoveFromGroupAsync não precisa ser chamado no OnDisconnectedAsync, ele é tratado automaticamente para você.
Aviso
Aviso de segurança: expor ConnectionId
pode levar a usurpação de identidade mal-intencionada se a versão do servidor ou do cliente do SignalR for ASP.NET Core 2.2 ou anterior.
Tratar erros
Exceções geradas em seus métodos de hub são enviadas ao cliente que invocou o método . No cliente JavaScript, o método invoke
retorna um JavaScript Promise
. Quando o cliente recebe um erro com um manipulador anexado à promessa usando catch
, ele é invocado e passado como um objeto JavaScript Error
:
connection.invoke("SendMessage", user, message).catch(err => console.error(err));
Se o Hub gerar uma exceção, as conexões não serão fechadas. Por padrão, SignalR retorna uma mensagem de erro genérica para o cliente. Por exemplo:
Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'MethodName' on the server.
Exceções inesperadas geralmente contêm informações confidenciais, como o nome de um servidor de banco de dados em uma exceção disparada quando a conexão de banco de dados falha. SignalR não expõe essas mensagens de erro detalhadas por padrão como uma medida de segurança. Para obter mais informações sobre por que os detalhes da exceção são suprimidos, confira as Considerações de segurança no ASP.NET Core SignalR.
Se você tiver uma condição excepcional que deseja propagar para o cliente, poderá usar a classe HubException. Se você lançar um HubException
a partir do seu método de hub, SignalRenviará toda a mensagem para o cliente, sem modificações:
public Task ThrowException()
{
throw new HubException("This error will be sent to the client!");
}
Observação
SignalR apenas envia a propriedade Message
da exceção para o cliente. O rastreamento de pilha e outras propriedades na exceção não estão disponíveis para o cliente.
Recursos adicionais
Por Rachel Appel e Kevin Griffin
Exibir ou baixar código de exemplo (como baixar)
O que é um hub do SignalR
A API SignalR Hubs permite que clientes conectados chamem métodos no servidor, facilitando a comunicação em tempo real. O servidor define métodos que são chamados pelo cliente e o cliente define métodos que são chamados pelo servidor. SignalR também possibilita a comunicação indireta cliente a cliente, sempre mediada pelo SignalR Hub, permitindo o envio de mensagens entre clientes individuais, grupos ou para todos os clientes conectados. O SignalR cuida de tudo o que é necessário para possibilitar a comunicação em tempo real entre cliente e servidor e entre servidor e cliente.
Configurar hubs do SignalR
O middleware SignalR requer alguns serviços, que são configurados chamando AddSignalR:
services.AddSignalR();
Ao adicionar a funcionalidade SignalR a um aplicativo ASP.NET Core, configure rotas SignalR chamando UseSignalR no método Startup.Configure
:
app.UseSignalR(route =>
{
route.MapHub<ChatHub>("/chathub");
});
Criar e usar hubs
Crie um hub declarando uma classe que herda de Hub e adicione métodos públicos a ele. Os clientes podem chamar métodos definidos como public
:
public class ChatHub : Hub
{
public Task SendMessage(string user, string message)
{
return Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
Você pode especificar um tipo de retorno e parâmetros, incluindo tipos complexos e matrizes, como faria em qualquer método C#. SignalR manipula a serialização e desserialização de objetos complexos e matrizes em seus parâmetros e valores retornados.
Observação
Os hubs são transitórios:
- Não armazene o estado em uma propriedade na classe de hub. Toda chamada de método de hub é executada em uma nova instância de hub.
- Não instancie um hub diretamente por meio de injeção de dependência. Para enviar mensagens para um cliente de outro lugar em seu aplicativo, use um
IHubContext
. - Use
await
ao chamar métodos assíncronos que dependem de o hub permanecer ativo. Por exemplo, um método comoClients.All.SendAsync(...)
pode falhar se for chamado semawait
e o método de hub for concluído antes queSendAsync
termine.
O objeto Context
A classe Hub tem uma propriedade Context que contém as seguintes propriedades com informações sobre a conexão:
Propriedade | Descrição |
---|---|
ConnectionId | Obtém a ID exclusiva para a conexão, atribuída por SignalR. Há uma ID de conexão para cada conexão. |
UserIdentifier | Obtém o identificador de usuário. Por padrão, SignalR usa o ClaimTypes.NameIdentifier do ClaimsPrincipal associado à conexão como o identificador de usuário. |
User | Obtém o ClaimsPrincipal associado ao usuário atual. |
Items | Obtém uma coleção de chave/valor que pode ser usada para compartilhar dados dentro do escopo dessa conexão. Os dados podem ser armazenados nesta coleção e persistirão para a conexão em diferentes invocações de método de hub. |
Features | Obtém a coleção de recursos disponíveis na conexão. Por enquanto, essa coleção não é necessária na maioria dos cenários, portanto, ainda não está documentada em detalhes. |
ConnectionAborted | Obtém um CancellationToken que notifica quando a conexão é anulada. |
Hub.Context também contém os seguintes métodos:
Método | Descrição |
---|---|
GetHttpContext | Retorna o HttpContext para a conexão, ou null se a conexão não está associada a uma solicitação HTTP. Para conexões HTTP, você pode usar esse método para obter informações como cabeçalhos HTTP e cadeias de caracteres de consulta. |
Abort | Anula a conexão. |
O objeto Clients
A classe Hub tem uma propriedade Clients que contém as seguintes propriedades para comunicação entre o servidor e o cliente:
Propriedade | Descrição |
---|---|
All | Chama um método em todos os clientes conectados |
Caller | Chama um método no cliente que invocou o método de hub |
Others | Chama um método em todos os clientes conectados, exceto o cliente que invocou o método |
Hub.Clients também contém os seguintes métodos:
Método | Descrição |
---|---|
AllExcept | Chama um método em todos os clientes conectados, exceto para as conexões especificadas |
Client | Chama um método em um cliente conectado específico |
Clients | Chama um método em clientes conectados específicos |
Group | Chama um método em todas as conexões no grupo especificado |
GroupExcept | Chama um método em todas as conexões no grupo especificado, exceto as conexões especificadas |
Groups | Chama um método em vários grupos de conexões |
OthersInGroup | Chama um método em um grupo de conexões, excluindo o cliente que invocou o método de hub |
User | Chama um método em todas as conexões associadas a um usuário específico |
Users | Chama um método em todas as conexões associadas aos usuários especificados |
Cada propriedade ou método nas tabelas anteriores retorna um objeto com um método SendAsync
. O método SendAsync
permite que você forneça o nome e os parâmetros do método do cliente a ser chamado.
Enviar mensagens para clientes
Para fazer chamadas para clientes específicos, use as propriedades do objeto Clients
. No exemplo a seguir, existem três Métodos de hub:
SendMessage
envia uma mensagem para todos os clientes conectados, usandoClients.All
.SendMessageToCaller
envia uma mensagem de volta para o chamador, usandoClients.Caller
.SendMessageToGroup
envia uma mensagem para todos os clientes no grupoSignalR Users
.
public Task SendMessage(string user, string message)
{
return Clients.All.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToCaller(string user, string message)
{
return Clients.Caller.SendAsync("ReceiveMessage", user, message);
}
public Task SendMessageToGroup(string user, string message)
{
return Clients.Group("SignalR Users").SendAsync("ReceiveMessage", user, message);
}
Hubs fortemente tipados
Uma desvantagem de usar SendAsync
é que ele depende de uma cadeia de caracteres mágica para especificar o método do cliente a ser chamado. Isso deixará o código aberto para erros de runtime se o nome do método estiver escrito incorretamente ou estiver ausente no cliente.
Uma alternativa ao uso do SendAsync
é tipar fortemente a Hub com Hub<T>. No exemplo a seguir, os métodos do cliente ChatHub
foram extraídos para uma interface chamada IChatClient
.
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
Essa interface pode ser usada para refatorar o exemplo ChatHub
anterior:
public class StronglyTypedChatHub : Hub<IChatClient>
{
public async Task SendMessage(string user, string message)
{
await Clients.All.ReceiveMessage(user, message);
}
public Task SendMessageToCaller(string user, string message)
{
return Clients.Caller.ReceiveMessage(user, message);
}
}
Usar Hub<IChatClient>
habilita a verificação em tempo de compilação dos métodos do cliente. Isso impede problemas causados pelo uso de cadeias de caracteres mágicas, pois Hub<T>
só pode fornecer acesso aos métodos definidos na interface.
Usar um Hub<T>
fortemente tipado desabilita a capacidade de usar SendAsync
. Todos os métodos definidos na interface ainda podem ser definidos como assíncronos. Na verdade, cada um desses métodos deve retornar um Task
. Como é uma interface, não use a palavra-chave async
. Por exemplo:
public interface IClient
{
Task ClientMethod();
}
Observação
O sufixo Async
não é removido do nome do método. A menos que o método do cliente seja definido com .on('MyMethodAsync')
, você não deve usar MyMethodAsync
como um nome.
Alterar o nome de um método de hub
Por padrão, um nome de método de hub do servidor é o nome do método .NET. No entanto, você pode usar o atributo HubMethodName para alterar esse padrão e especificar manualmente um nome para o método. O cliente deve usar esse nome, em vez do nome do método .NET, ao invocar o método :
[HubMethodName("SendMessageToUser")]
public Task DirectMessage(string user, string message)
{
return Clients.User(user).SendAsync("ReceiveMessage", user, message);
}
Manipular eventos para uma conexão
A API Hubs do SignalR fornece os métodos virtuais OnConnectedAsync e OnDisconnectedAsync para gerenciar e acompanhar conexões. Substitua o método virtual OnConnectedAsync
para executar ações quando um cliente se conectar ao Hub, como adicioná-lo a um grupo:
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
Substitua o método virtual OnDisconnectedAsync
para executar ações quando um cliente se desconectar. Se o cliente se desconectar intencionalmente (chamando connection.stop()
, por exemplo), o parâmetro exception
será null
. No entanto, se o cliente estiver desconectado devido a um erro (como uma falha de rede), o parâmetro exception
conterá uma exceção que descreve a falha:
public override async Task OnDisconnectedAsync(Exception exception)
{
await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", "I", "disconnect");
await base.OnDisconnectedAsync(exception);
}
RemoveFromGroupAsync não precisa ser chamado no OnDisconnectedAsync, ele é tratado automaticamente para você.
Aviso
Aviso de segurança: expor ConnectionId
pode levar a usurpação de identidade mal-intencionada se a versão do servidor ou do cliente do SignalR for ASP.NET Core 2.2 ou anterior.
Tratar erros
Exceções geradas em seus métodos de hub são enviadas ao cliente que invocou o método . No cliente JavaScript, o método invoke
retorna um JavaScript Promise
. Quando o cliente recebe um erro com um manipulador anexado à promessa usando catch
, ele é invocado e passado como um objeto JavaScript Error
:
connection.invoke("SendMessage", user, message).catch(err => console.error(err));
Se o Hub gerar uma exceção, as conexões não serão fechadas. Por padrão, SignalR retorna uma mensagem de erro genérica para o cliente. Por exemplo:
Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'MethodName' on the server.
Exceções inesperadas geralmente contêm informações confidenciais, como o nome de um servidor de banco de dados em uma exceção disparada quando a conexão de banco de dados falha. SignalR não expõe essas mensagens de erro detalhadas por padrão como uma medida de segurança. Para obter mais informações sobre por que os detalhes da exceção são suprimidos, confira as Considerações de segurança no ASP.NET Core SignalR.
Se você tiver uma condição excepcional que deseja propagar para o cliente, poderá usar a classe HubException. Se você lançar um HubException
a partir do seu método de hub, SignalRenviará toda a mensagem para o cliente, sem modificações:
public Task ThrowException()
{
throw new HubException("This error will be sent to the client!");
}
Observação
SignalR apenas envia a propriedade Message
da exceção para o cliente. O rastreamento de pilha e outras propriedades na exceção não estão disponíveis para o cliente.