Usar hubs no SignalR para ASP.NET Core

Por Rachel Appel e Kevin Griffin

A API Hubs do SignalR permite que clientes conectados chamem métodos no servidor. O servidor define métodos que são chamados a partir do cliente e o cliente define métodos que são chamados a partir do servidor. 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 como Clients.All.SendAsync(...) pode falhar se for chamado sem await e o método de hub for concluído antes que SendAsync 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, usando Clients.All.
  • SendMessageToCaller envia uma mensagem de volta para o chamador, usando Clients.Caller.
  • SendMessageToGroup envia uma mensagem para todos os clientes no grupo SignalR 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 Hubs do SignalR permite que clientes conectados chamem métodos no servidor. O servidor define métodos que são chamados a partir do cliente e o cliente define métodos que são chamados a partir do servidor. 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 como Clients.All.SendAsync(...) pode falhar se for chamado sem await e o método de hub for concluído antes que SendAsync 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, usando Clients.All.
  • SendMessageToCaller envia uma mensagem de volta para o chamador, usando Clients.Caller.
  • SendMessageToGroup envia uma mensagem para todos os clientes no grupo SignalR 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 Hubs do SignalR permite que clientes conectados chamem métodos no servidor. O servidor define métodos que são chamados a partir do cliente e o cliente define métodos que são chamados a partir do servidor. 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 como Clients.All.SendAsync(...) pode falhar se for chamado sem await e o método de hub for concluído antes que SendAsync 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, usando Clients.All.
  • SendMessageToCaller envia uma mensagem de volta para o chamador, usando Clients.Caller.
  • SendMessageToGroup envia uma mensagem para todos os clientes no grupo SignalR 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 fazer download do código de exemplo(como fazer download)

O que é um hub do SignalR

A API Hubs do SignalR permite chamar métodos em clientes conectados a partir do servidor. No código do servidor, você define métodos que são chamados pelo cliente. No código do cliente, você define métodos que são chamados pelo servidor. SignalR cuida de tudo nos bastidores que possibilita comunicações 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 como Clients.All.SendAsync(...) pode falhar se for chamado sem await e o método de hub for concluído antes que SendAsync 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, usando Clients.All.
  • SendMessageToCaller envia uma mensagem de volta para o chamador, usando Clients.Caller.
  • SendMessageToGroup envia uma mensagem para todos os clientes no grupo SignalR 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 fazer download do código de exemplo(como fazer download)

O que é um hub do SignalR

A API Hubs do SignalR permite chamar métodos em clientes conectados a partir do servidor. No código do servidor, você define métodos que são chamados pelo cliente. No código do cliente, você define métodos que são chamados pelo servidor. SignalR cuida de tudo nos bastidores que possibilita comunicações 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 como Clients.All.SendAsync(...) pode falhar se for chamado sem await e o método de hub for concluído antes que SendAsync 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, usando Clients.All.
  • SendMessageToCaller envia uma mensagem de volta para o chamador, usando Clients.Caller.
  • SendMessageToGroup envia uma mensagem para todos os clientes no grupo SignalR 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