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
awaital 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étodoClients.All.SendAsync(...)central se completa antes de queawaitSendAsyncfinalice.
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:
SendMessageenvía un mensaje a todos los clientes conectados medianteClients.All.SendMessageToCallerdevuelve un mensaje al autor de la llamada medianteClients.Caller.SendMessageToGroupenvía un mensaje a todos los clientes delSignalR Usersgrupo.
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.