Uso de concentradores en SignalR para ASP.NET Core

Por Kevin Appel y Kevin Asíns

Vea o descargue el código de ejemplo (cómo descargarlo)

¿Qué es un SignalR centro?

La SignalR API de Hubs permite llamar a métodos en clientes conectados desde el servidor. En el código del servidor, se definen métodos a los que llama el cliente. En el código de cliente, se definen métodos a los que se llama desde el servidor. SignalR se encarga de todo lo que está en segundo plano que hace posibles las comunicaciones de cliente a servidor y de servidor a cliente en tiempo real.

Configuración SignalR de centros

El SignalR middleware requiere algunos servicios, que se configuran mediante una llamada a services.AddSignalR .

services.AddSignalR();

Al agregar funcionalidad a una ASP.NET Core, configure las rutas mediante una llamada SignalR SignalR a en la endpoint.MapHub Startup.Configure devolución de llamada del app.UseEndpoints método.

app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.MapHub<ChatHub>("/chathub");
});

Al agregar SignalR funcionalidad a una ASP.NET Core, configure las SignalR rutas mediante una llamada a en app.UseSignalR el método Startup.Configure .

app.UseSignalR(route =>
{
    route.MapHub<ChatHub>("/chathub");
});

Creación y uso de centros

Cree un centro declarando una clase que herede de Hub y agregue métodos públicos a él. Los clientes pueden llamar a métodos definidos como public .

public class ChatHub : Hub
{
    public Task SendMessage(string user, string message)
    {
        return Clients.All.SendAsync("ReceiveMessage", user, message);
    }
}

Puede especificar un tipo de valor devuelto y parámetros, incluidos tipos complejos y matrices, como lo haría en cualquier método de C#. SignalR controla la serialización y deserialización de objetos complejos y matrices en los parámetros y valores devueltos.

Nota

Los concentradores son transitorios:

  • No almacene el estado en una propiedad de la clase central. Cada llamada al método de concentrador se ejecuta en una nueva instancia de concentrador.
  • Use await al llamar a métodos asincrónicos que dependen de que el centro se mantiene activo. Por ejemplo, un método como puede producir un error si se llama sin y el método Clients.All.SendAsync(...) central se completa antes de que await SendAsync finalice.

El objeto Context

La Hub clase tiene una propiedad que contiene las siguientes propiedades con información sobre la Context conexión:

Propiedad Descripción
ConnectionId Obtiene el identificador único de la conexión, asignado por SignalR . Hay un identificador de conexión para cada conexión.
UserIdentifier Obtiene el identificador de usuario. De forma predeterminada, SignalR usa del asociado a la conexión como identificador de ClaimTypes.NameIdentifier ClaimsPrincipal usuario.
User Obtiene el ClaimsPrincipal asociado al usuario actual.
Items Obtiene una colección de clave/valor que se puede usar para compartir datos dentro del ámbito de esta conexión. Los datos se pueden almacenar en esta colección y se conservarán para la conexión a través de diferentes invocaciones de método de concentrador.
Features Obtiene la colección de características disponibles en la conexión. Por ahora, esta colección no es necesaria en la mayoría de los escenarios, por lo que aún no se documenta en detalle.
ConnectionAborted Obtiene un CancellationToken objeto que notifica cuándo se anula la conexión.

Hub.Context también contiene los métodos siguientes:

Método Descripción
GetHttpContext Devuelve para HttpContext la conexión o si la conexión no está asociada a una solicitud null HTTP. Para las conexiones HTTP, puede usar este método para obtener información como encabezados HTTP y cadenas de consulta.
Abort Anula la conexión.

El objeto Clients

La clase tiene una propiedad que contiene las siguientes propiedades para la comunicación Hub entre el servidor y el Clients cliente:

Propiedad Descripción
All Llama a un método en todos los clientes conectados
Caller Llama a un método en el cliente que invocó el método de concentrador.
Others Llama a un método en todos los clientes conectados, excepto el cliente que invocó el método .

Hub.Clients también contiene los métodos siguientes:

Método Descripción
AllExcept Llama a un método en todos los clientes conectados, excepto en las conexiones especificadas.
Client Llama a un método en un cliente conectado específico
Clients Llama a un método en clientes conectados específicos
Group Llama a un método en todas las conexiones del grupo especificado.
GroupExcept Llama a un método en todas las conexiones del grupo especificado, excepto en las conexiones especificadas.
Groups Llama a un método en varios grupos de conexiones
OthersInGroup Llama a un método en un grupo de conexiones, excepto el cliente que invocó el método de concentrador.
User Llama a un método en todas las conexiones asociadas a un usuario específico
Users Llama a un método en todas las conexiones asociadas a los usuarios especificados.

Cada propiedad o método de las tablas anteriores devuelve un objeto con un SendAsync método . El SendAsync método permite proporcionar el nombre y los parámetros del método de cliente al que se va a llamar.

Envío de mensajes a clientes

Para realizar llamadas a clientes específicos, use las propiedades del Clients objeto . En el ejemplo siguiente, hay tres métodos hub:

  • SendMessage envía un mensaje a todos los clientes conectados mediante Clients.All .
  • SendMessageToCaller devuelve un mensaje al autor de la llamada mediante Clients.Caller .
  • SendMessageToGroup envía un mensaje a todos los clientes del SignalR Users grupo.
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);
}

Concentradores fuertemente con tipo

Un inconveniente de usar es que se basa en una cadena mágica para especificar el método de cliente al SendAsync que se va a llamar. Esto deja código abierto a errores en tiempo de ejecución si el nombre del método está mal escrito o falta en el cliente.

Una alternativa a usar SendAsync es escribir fuertemente Hub con Hub<T> . En el ejemplo siguiente, los métodos de cliente se han ChatHub extraído en una interfaz denominada IChatClient .

public interface IChatClient
{
    Task ReceiveMessage(string user, string message);
}

Esta interfaz se puede usar para refactorizar el ejemplo 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);
    }
}

El Hub<IChatClient> uso de habilita la comprobación en tiempo de compilación de los métodos de cliente. Esto evita problemas causados por el uso de cadenas mágicas, ya que solo puede proporcionar acceso a Hub<T> los métodos definidos en la interfaz .

El uso de un fuertemente especificado Hub<T> deshabilita la capacidad de usar SendAsync . Los métodos definidos en la interfaz todavía se pueden definir como asincrónicos. De hecho, cada uno de estos métodos debe devolver Task . Como es una interfaz, no use la palabra async clave . Por ejemplo:

public interface IClient
{
    Task ClientMethod();
}

Nota

El Async sufijo no se quita del nombre del método. A menos que el método de cliente se defina con .on('MyMethodAsync') , no debe usar como MyMethodAsync nombre.

Cambio del nombre de un método de concentrador

De forma predeterminada, un nombre de método del centro de servidores es el nombre del método .NET. Sin embargo, puede usar el atributo HubMethodName para cambiar este valor predeterminado y especificar manualmente un nombre para el método. El cliente debe usar este nombre, en lugar del nombre del método .NET, al invocar el método .

[HubMethodName("SendMessageToUser")]
public Task DirectMessage(string user, string message)
{
    return Clients.User(user).SendAsync("ReceiveMessage", user, message);
}

Control de eventos para una conexión

La SignalR API de Hubs proporciona los OnConnectedAsync métodos virtuales y para administrar y realizar OnDisconnectedAsync un seguimiento de las conexiones. Invalide el método virtual para realizar acciones cuando un cliente se conecte al concentrador, como OnConnectedAsync agregarlo a un grupo.

public override async Task OnConnectedAsync()
{
    await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
    await base.OnConnectedAsync();
}

Invalide OnDisconnectedAsync el método virtual para realizar acciones cuando un cliente se desconecte. Si el cliente se desconecta intencionadamente (llamando a connection.stop() , por ejemplo), exception el parámetro será null . Sin embargo, si el cliente está desconectado debido a un error (por ejemplo, un error de red), el parámetro contendrá una excepción exception que describe el error.

public override async Task OnDisconnectedAsync(Exception exception)
{
    await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users");
    await base.OnDisconnectedAsync(exception);
}

Advertencia

Advertencia de seguridad: la exposición puede dar lugar a suplantación malintencionada si la versión del servidor o cliente ConnectionId SignalR se ASP.NET Core 2.2 o anterior.

errores

Las excepciones que se inician en los métodos centrales se envían al cliente que invocó el método . En el cliente de JavaScript, el invoke método devuelve una promesa de JavaScript. Cuando el cliente recibe un error con un controlador asociado a la promesa mediante , se invoca y se catch pasa como un objeto de Error JavaScript.

connection.invoke("SendMessage", user, message).catch(err => console.error(err));

Si el centro produce una excepción, las conexiones no se cierran. De forma predeterminada, SignalR devuelve un mensaje de error genérico al cliente. Por ejemplo:

Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'MethodName' on the server.

Las excepciones inesperadas suelen contener información confidencial, como el nombre de un servidor de bases de datos en una excepción desencadenada cuando se produce un error en la conexión de la base de datos. SignalR no expone estos mensajes de error detallados de forma predeterminada como medida de seguridad. Consulte el artículo Consideraciones de seguridad para obtener más información sobre por qué se suprimen los detalles de excepción.

Si tiene una condición excepcional que desea propagar al cliente, puede usar la HubException clase . Si inicia un desde el método del centro, enviará el mensaje completo HubException SignalR al cliente, sin modificar.

public Task ThrowException()
{
    throw new HubException("This error will be sent to the client!");
}

Nota

SignalR solo envía la Message propiedad de la excepción al cliente. El seguimiento de la pila y otras propiedades de la excepción no están disponibles para el cliente.