Uso de centros de conectividad en SignalR para ASP.NET Core
Por Rachel Appel y Kevin Griffin
La API de centros de conectividad SignalR permite a los clientes conectados llamar a métodos del servidor, lo que facilita la comunicación en tiempo real. El servidor define los métodos a los que llama el cliente y el cliente define los métodos a los que llama el servidor. SignalR también permite la comunicación indirecta de cliente a cliente, siempre mediada por el centro de SignalR, lo que permite enviar mensajes entre clientes individuales, grupos o a todos los clientes conectados. SignalR se encarga de todo lo necesario para hacer posible la comunicación de cliente a servidor y servidor a cliente en tiempo real.
Configuración de centros de conectividad de SignalR
Para registrar los servicios requeridos por centros de conectividad de SignalR, llame a AddSignalR en Program.cs
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
Para configurar puntos de conexión de SignalR, llame a MapHub, también en Program.cs
:
app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");
app.Run();
Nota:
Los ensamblados del lado servidor de SignalR de ASP.NET Core ahora están instalados con el SDK de .NET Core. Consulte ensamblados de SignalR en el marco compartido para más información.
Creación y uso de centros de conectividad
Cree un centro de conectividad declarando una clase que hereda de Hub. Agregue métodos public
a la clase para que se puedan llamar desde los clientes:
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
}
Nota
Los centros de conectividad son transitorios:
- No almacene el estado en una propiedad de la clase de centro de conectividad. Cada llamada al método de centro de conectividad se ejecuta en una nueva instancia del centro de conectividad.
- No cree una instancia de un centro directamente a través de la inserción de dependencias. Para enviar mensajes a un cliente desde otro lugar de la aplicación, use
IHubContext
. - Use
await
cuando llame a métodos asíncronos que dependan de que el centro de conectividad permanezca activo. Por ejemplo, un método comoClients.All.SendAsync(...)
puede fallar si se llama sinawait
y el método del centro de conectividad termina antes de queSendAsync
termine.
Objeto Context
La clase Hub incluye una propiedad Context que contiene las siguientes propiedades con información sobre la 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 manera predeterminada, SignalR usa el ClaimTypes.NameIdentifier del ClaimsPrincipal asociado a la conexión como identificador de 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 centro de conectividad. |
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 con detalle. |
ConnectionAborted | Obtiene un CancellationToken que notifica cuando se anula la conexión. |
Hub.Context también contiene los siguientes métodos:
Método | Descripción |
---|---|
GetHttpContext | Devuelve HttpContext para la conexión, o null si la conexión no está asociada a una solicitud HTTP. Para las conexiones HTTP, use este método para obtener información como encabezados HTTP y cadenas de consulta. |
Abort | Anula la conexión. |
El objeto Clients
La clase Hub incluye una propiedad Clients que contiene las siguientes propiedades para la comunicación entre servidor y 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 centro de conectividad |
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 siguientes métodos:
Método | Descripción |
---|---|
AllExcept | Llama a un método en todos los clientes conectados, excepto para 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 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 centro de conectividad |
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 método SendAsync
. El método SendAsync
recibe el nombre del método de cliente a llamar y cualquier parámetro.
El objeto devuelto por los métodos Client
y Caller
también contiene un método InvokeAsync
, que puede usarse para esperar un resultado del cliente.
Envío de mensajes a clientes
Para hacer llamadas a clientes específicos, use las propiedades del objeto Clients
. En el ejemplo siguiente, hay tres métodos de centro de conectividad:
SendMessage
envía un mensaje a todos los clientes conectados medianteClients.All
.SendMessageToCaller
devuelve un mensaje al autor de la llamada medianteClients.Caller
.SendMessageToGroup
envía un mensaje a todos los clientes del 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);
Concentradores fuertemente tipados
Un inconveniente de usar SendAsync
es que se basa en una cadena para especificar el método de cliente al que se va a llamar. Esto deja el código abierto a errores de runtime si el nombre del método está mal escrito o falta en el cliente.
Una alternativa a usar SendAsync
es tipar fuertemente la clase Hub con Hub<T>. En el siguiente ejemplo, el método cliente ChatHub
se ha extraído en una interfaz llamada IChatClient
:
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
Esta interfaz se puede usar para refactorizar el ejemplo anterior ChatHub
para que esté fuertemente 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);
}
El uso de Hub<IChatClient>
habilita la comprobación en tiempo de compilación de los métodos del cliente. Esto evita problemas causados por el uso de cadenas, ya que Hub<T>
solo puede proporcionar acceso a los métodos definidos en la interfaz. El uso de un Hub<T>
fuertemente tipado deshabilita la capacidad de usar SendAsync
.
Nota:
El sufijo Async
no se elimina de los nombres de los métodos. A menos que se defina un método de cliente con .on('MyMethodAsync')
, no use MyMethodAsync
como nombre.
Resultados del cliente
Además de realizar llamadas a clientes, el servidor puede solicitar un resultado de un cliente. Esto requiere que el servidor use ISingleClientProxy.InvokeAsync
y que el cliente devuelva un resultado de su controlador .On
.
Hay dos maneras de usar la API en el servidor, la primera consiste en llamar a Client(...)
o Caller
en la propiedad Clients
en un método de centro de conectividad:
public class ChatHub : Hub
{
public async Task<string> WaitForMessage(string connectionId)
{
var message = await Clients.Client(connectionId).InvokeAsync<string>(
"GetMessage");
return message;
}
}
La segunda manera es llamar a Client(...)
en una instancia de IHubContext<T>
:
async Task SomeMethod(IHubContext<MyHub> context)
{
string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
"GetMessage");
}
Los centros de conectividad fuertemente tipados también pueden devolver valores de métodos de interfaz:
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;
}
}
Los clientes devuelven resultados en sus controladores de .On(...)
, como se muestra a continuación:
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 de Java
hubConnection.onWithResult("GetMessage", () -> {
return Single.just("message");
});
Cambio del nombre de un método de centro de conectividad
De manera predeterminada, el nombre del método de un centro de conectividad del servidor es el nombre del método .NET. Para cambiar este comportamiento predeterminado para un método específico, use el atributo HubMethodName. El cliente debe usar este nombre en lugar del nombre del método de .NET al invocar el método :
[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
=> await Clients.User(user).SendAsync("ReceiveMessage", user, message);
Inyección de servicios en un centro de conectividad
Los constructores de concentradores pueden aceptar servicios de inserción de dependencias como parámetros, que pueden almacenarse en propiedades de la clase para su uso en un método de concentrador.
Al insertar varios servicios para diferentes métodos de centro de conectividad o como una manera alternativa de escribir código, los métodos de centro de conectividad también pueden aceptar servicios de DI. De forma predeterminada, los parámetros del método de centro de conectividad se inspeccionan y resuelven desde la inserción de dependencias si es posible.
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);
}
}
Si no se desea la resolución implícita de parámetros desde servicios, desactívela con DisableImplicitFromServicesParameters.
Para especificar explícitamente qué parámetros se resuelven desde DI en los métodos del centro de conectividad, use la opción DisableImplicitFromServicesParameters
y use el atributo [FromServices]
o un atributo personalizado que implemente IFromServiceMetadata
en los parámetros del método del centro de conectividad que deberían estar resueltos desde 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);
}
}
Nota:
Esta característica usa IServiceProviderIsService, que es opcional en las implementaciones de DI. Si el contenedor de inserción de dependencias de la aplicación no admite esta característica, no se admite la inserción de servicios en métodos de centro de conectividad.
Compatibilidad de servicios con claves en la inserción de dependencias
Los servicios con claves hacen referencia a un mecanismo para registrar y recuperar servicios de inserción de dependencias (DI) mediante claves. Un servicio se asocia a una clave llamando AddKeyedSingleton a (o AddKeyedScoped
o AddKeyedTransient
) para registrarlo. Acceda a un servicio registrado especificando la clave con el atributo [FromKeyedServices]
. En el código siguiente se muestra cómo usar servicios con claves:
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"));
}
}
Control de eventos de una conexión
La API de centros de conectividad de SignalR proporciona los métodos virtuales OnConnectedAsync y OnDisconnectedAsync para administrar y realizar un seguimiento de las conexiones. Sobrescriba el método virtual OnConnectedAsync
para realizar acciones cuando un cliente se conecta al centro de conectividad, como añadirlo a un grupo:
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
Sobrescriba el método virtual OnDisconnectedAsync
para realizar acciones cuando un cliente se desconecta. Si el cliente se desconecta intencionadamente, por ejemplo llamando a connection.stop()
, el parámetro exception
se establece en null
. Sin embargo, si el cliente se desconecta debido a un error, como un fallo de red, el parámetro exception
contiene una excepción que describe el fallo:
public override async Task OnDisconnectedAsync(Exception? exception)
{
await base.OnDisconnectedAsync(exception);
}
No es necesario llamar a RemoveFromGroupAsync en OnDisconnectedAsync, la llamada se realiza de forma automática.
Control de errores
Las excepciones iniciadas en los métodos concentradores se envían al cliente que invocó el método . En el cliente de JavaScript, el método invoke
devuelve un Promise
de JavaScript. Los clientes pueden adjuntar un controlador de catch
a la promesa devuelta o usar try
/catch
con async
/await
para controlar excepciones:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Las conexiones no se cierran cuando un centro produce una excepción. De forma predeterminada, SignalR devuelve un mensaje de error genérico al cliente, como se muestra en el ejemplo siguiente:
Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' 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 base de datos. SignalR no expone estos mensajes de error detallados de forma predeterminada como medida de seguridad. Para más información sobre por qué se suprimen los detalles de las excepciones, consulte Consideraciones de seguridad en ASP.NET Core SignalR.
Si se debe propagar una condición excepcional al cliente, use la clase HubException. Si se produce un HubException
en un método de centro de conectividad, SignalRenvía el mensaje de excepción completo al cliente, sin modificar:
public Task ThrowException()
=> throw new HubException("This error will be sent to the client!");
Nota
SignalR solo envía la propiedad Message
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.
Recursos adicionales
Por Rachel Appel y Kevin Griffin
La API de centros de conectividad SignalR permite a los clientes conectados llamar a métodos del servidor, lo que facilita la comunicación en tiempo real. El servidor define los métodos a los que llama el cliente y el cliente define los métodos a los que llama el servidor. SignalR también permite la comunicación indirecta de cliente a cliente, siempre mediada por el centro de SignalR, lo que permite enviar mensajes entre clientes individuales, grupos o a todos los clientes conectados. SignalR se encarga de todo lo necesario para hacer posible la comunicación de cliente a servidor y servidor a cliente en tiempo real.
Configuración de centros de conectividad de SignalR
Para registrar los servicios requeridos por centros de conectividad de SignalR, llame a AddSignalR en Program.cs
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
Para configurar puntos de conexión de SignalR, llame a MapHub, también en Program.cs
:
app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");
app.Run();
Nota:
Los ensamblados del lado servidor de SignalR de ASP.NET Core ahora están instalados con el SDK de .NET Core. Consulte ensamblados de SignalR en el marco compartido para más información.
Creación y uso de centros de conectividad
Cree un centro de conectividad declarando una clase que hereda de Hub. Agregue métodos public
a la clase para que se puedan llamar desde los clientes:
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
}
Nota
Los centros de conectividad son transitorios:
- No almacene el estado en una propiedad de la clase de centro de conectividad. Cada llamada al método de centro de conectividad se ejecuta en una nueva instancia del centro de conectividad.
- No cree una instancia de un centro directamente a través de la inserción de dependencias. Para enviar mensajes a un cliente desde otro lugar de la aplicación, use
IHubContext
. - Use
await
cuando llame a métodos asíncronos que dependan de que el centro de conectividad permanezca activo. Por ejemplo, un método comoClients.All.SendAsync(...)
puede fallar si se llama sinawait
y el método del centro de conectividad termina antes de queSendAsync
termine.
Objeto Context
La clase Hub incluye una propiedad Context que contiene las siguientes propiedades con información sobre la 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 manera predeterminada, SignalR usa el ClaimTypes.NameIdentifier del ClaimsPrincipal asociado a la conexión como identificador de 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 centro de conectividad. |
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 con detalle. |
ConnectionAborted | Obtiene un CancellationToken que notifica cuando se anula la conexión. |
Hub.Context también contiene los siguientes métodos:
Método | Descripción |
---|---|
GetHttpContext | Devuelve HttpContext para la conexión, o null si la conexión no está asociada a una solicitud HTTP. Para las conexiones HTTP, use este método para obtener información como encabezados HTTP y cadenas de consulta. |
Abort | Anula la conexión. |
El objeto Clients
La clase Hub incluye una propiedad Clients que contiene las siguientes propiedades para la comunicación entre servidor y 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 centro de conectividad |
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 siguientes métodos:
Método | Descripción |
---|---|
AllExcept | Llama a un método en todos los clientes conectados, excepto para 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 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 centro de conectividad |
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 método SendAsync
. El método SendAsync
recibe el nombre del método de cliente a llamar y cualquier parámetro.
El objeto devuelto por los métodos Client
y Caller
también contiene un método InvokeAsync
, que puede usarse para esperar un resultado del cliente.
Envío de mensajes a clientes
Para hacer llamadas a clientes específicos, use las propiedades del objeto Clients
. En el ejemplo siguiente, hay tres métodos de centro de conectividad:
SendMessage
envía un mensaje a todos los clientes conectados medianteClients.All
.SendMessageToCaller
devuelve un mensaje al autor de la llamada medianteClients.Caller
.SendMessageToGroup
envía un mensaje a todos los clientes del 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);
Concentradores fuertemente tipados
Un inconveniente de usar SendAsync
es que se basa en una cadena para especificar el método de cliente al que se va a llamar. Esto deja el código abierto a errores de runtime si el nombre del método está mal escrito o falta en el cliente.
Una alternativa a usar SendAsync
es tipar fuertemente la clase Hub con Hub<T>. En el siguiente ejemplo, el método cliente ChatHub
se ha extraído en una interfaz llamada IChatClient
:
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
Esta interfaz se puede usar para refactorizar el ejemplo anterior ChatHub
para que esté fuertemente 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);
}
El uso de Hub<IChatClient>
habilita la comprobación en tiempo de compilación de los métodos del cliente. Esto evita problemas causados por el uso de cadenas, ya que Hub<T>
solo puede proporcionar acceso a los métodos definidos en la interfaz. El uso de un Hub<T>
fuertemente tipado deshabilita la capacidad de usar SendAsync
.
Nota:
El sufijo Async
no se elimina de los nombres de los métodos. A menos que se defina un método de cliente con .on('MyMethodAsync')
, no use MyMethodAsync
como nombre.
Resultados del cliente
Además de realizar llamadas a clientes, el servidor puede solicitar un resultado de un cliente. Esto requiere que el servidor use ISingleClientProxy.InvokeAsync
y que el cliente devuelva un resultado de su controlador .On
.
Hay dos maneras de usar la API en el servidor, la primera consiste en llamar a Client(...)
o Caller
en la propiedad Clients
en un método de centro de conectividad:
public class ChatHub : Hub
{
public async Task<string> WaitForMessage(string connectionId)
{
var message = await Clients.Client(connectionId).InvokeAsync<string>(
"GetMessage");
return message;
}
}
La segunda manera es llamar a Client(...)
en una instancia de IHubContext<T>
:
async Task SomeMethod(IHubContext<MyHub> context)
{
string result = await context.Clients.Client(connectionID).InvokeAsync<string>(
"GetMessage");
}
Los centros de conectividad fuertemente tipados también pueden devolver valores de métodos de interfaz:
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;
}
}
Los clientes devuelven resultados en sus controladores de .On(...)
, como se muestra a continuación:
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 de Java
hubConnection.onWithResult("GetMessage", () -> {
return Single.just("message");
});
Cambio del nombre de un método de centro de conectividad
De manera predeterminada, el nombre del método de un centro de conectividad del servidor es el nombre del método .NET. Para cambiar este comportamiento predeterminado para un método específico, use el atributo HubMethodName. El cliente debe usar este nombre en lugar del nombre del método de .NET al invocar el método :
[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
=> await Clients.User(user).SendAsync("ReceiveMessage", user, message);
Inyección de servicios en un centro de conectividad
Los constructores de concentradores pueden aceptar servicios de inserción de dependencias como parámetros, que pueden almacenarse en propiedades de la clase para su uso en un método de concentrador.
Al insertar varios servicios para diferentes métodos de centro de conectividad o como una manera alternativa de escribir código, los métodos de centro de conectividad también pueden aceptar servicios de DI. De forma predeterminada, los parámetros del método de centro de conectividad se inspeccionan y resuelven desde la inserción de dependencias si es posible.
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);
}
}
Si no se desea la resolución implícita de parámetros desde servicios, desactívela con DisableImplicitFromServicesParameters.
Para especificar explícitamente qué parámetros se resuelven desde DI en los métodos del centro de conectividad, use la opción DisableImplicitFromServicesParameters
y use el atributo [FromServices]
o un atributo personalizado que implemente IFromServiceMetadata
en los parámetros del método del centro de conectividad que deberían estar resueltos desde 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);
}
}
Nota:
Esta característica usa IServiceProviderIsService, que es opcional en las implementaciones de DI. Si el contenedor de inserción de dependencias de la aplicación no admite esta característica, no se admite la inserción de servicios en métodos de centro de conectividad.
Control de eventos de una conexión
La API de centros de conectividad de SignalR proporciona los métodos virtuales OnConnectedAsync y OnDisconnectedAsync para administrar y realizar un seguimiento de las conexiones. Sobrescriba el método virtual OnConnectedAsync
para realizar acciones cuando un cliente se conecta al centro de conectividad, como añadirlo a un grupo:
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
Sobrescriba el método virtual OnDisconnectedAsync
para realizar acciones cuando un cliente se desconecta. Si el cliente se desconecta intencionadamente, por ejemplo llamando a connection.stop()
, el parámetro exception
se establece en null
. Sin embargo, si el cliente se desconecta debido a un error, como un fallo de red, el parámetro exception
contiene una excepción que describe el fallo:
public override async Task OnDisconnectedAsync(Exception? exception)
{
await base.OnDisconnectedAsync(exception);
}
No es necesario llamar a RemoveFromGroupAsync en OnDisconnectedAsync, se hace automáticamente.
Control de errores
Las excepciones iniciadas en los métodos concentradores se envían al cliente que invocó el método . En el cliente de JavaScript, el método invoke
devuelve un Promise
de JavaScript. Los clientes pueden adjuntar un controlador de catch
a la promesa devuelta o usar try
/catch
con async
/await
para controlar excepciones:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Las conexiones no se cierran cuando un centro produce una excepción. De forma predeterminada, SignalR devuelve un mensaje de error genérico al cliente, como se muestra en el ejemplo siguiente:
Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' 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 base de datos. SignalR no expone estos mensajes de error detallados de forma predeterminada como medida de seguridad. Para más información sobre por qué se suprimen los detalles de las excepciones, consulte Consideraciones de seguridad en ASP.NET Core SignalR.
Si se debe propagar una condición excepcional al cliente, use la clase HubException. Si se produce un HubException
en un método de centro de conectividad, SignalRenvía el mensaje de excepción completo al cliente, sin modificar:
public Task ThrowException()
=> throw new HubException("This error will be sent to the client!");
Nota
SignalR solo envía la propiedad Message
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.
Recursos adicionales
Por Rachel Appel y Kevin Griffin
La API de centros de conectividad SignalR permite a los clientes conectados llamar a métodos del servidor, lo que facilita la comunicación en tiempo real. El servidor define los métodos a los que llama el cliente y el cliente define los métodos a los que llama el servidor. SignalR también permite la comunicación indirecta de cliente a cliente, siempre mediada por el centro de SignalR, lo que permite enviar mensajes entre clientes individuales, grupos o a todos los clientes conectados. SignalR se encarga de todo lo necesario para hacer posible la comunicación de cliente a servidor y servidor a cliente en tiempo real.
Configuración de centros de conectividad de SignalR
Para registrar los servicios requeridos por centros de conectividad de SignalR, llame a AddSignalR en Program.cs
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
Para configurar puntos de conexión de SignalR, llame a MapHub, también en Program.cs
:
app.MapRazorPages();
app.MapHub<ChatHub>("/Chat");
app.Run();
Nota:
Los ensamblados del lado servidor de SignalR de ASP.NET Core ahora están instalados con el SDK de .NET Core. Consulte ensamblados de SignalR en el marco compartido para más información.
Creación y uso de centros de conectividad
Cree un centro de conectividad declarando una clase que hereda de Hub. Agregue métodos public
a la clase para que se puedan llamar desde los clientes:
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
=> await Clients.All.SendAsync("ReceiveMessage", user, message);
}
Nota
Los centros de conectividad son transitorios:
- No almacene el estado en una propiedad de la clase de centro de conectividad. Cada llamada al método de centro de conectividad se ejecuta en una nueva instancia del centro de conectividad.
- No cree una instancia de un centro directamente a través de la inserción de dependencias. Para enviar mensajes a un cliente desde otro lugar de la aplicación, use
IHubContext
. - Use
await
cuando llame a métodos asíncronos que dependan de que el centro de conectividad permanezca activo. Por ejemplo, un método comoClients.All.SendAsync(...)
puede fallar si se llama sinawait
y el método del centro de conectividad termina antes de queSendAsync
termine.
Objeto Context
La clase Hub incluye una propiedad Context que contiene las siguientes propiedades con información sobre la 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 manera predeterminada, SignalR usa el ClaimTypes.NameIdentifier del ClaimsPrincipal asociado a la conexión como identificador de 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 centro de conectividad. |
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 con detalle. |
ConnectionAborted | Obtiene un CancellationToken que notifica cuando se anula la conexión. |
Hub.Context también contiene los siguientes métodos:
Método | Descripción |
---|---|
GetHttpContext | Devuelve HttpContext para la conexión, o null si la conexión no está asociada a una solicitud HTTP. Para las conexiones HTTP, use este método para obtener información como encabezados HTTP y cadenas de consulta. |
Abort | Anula la conexión. |
El objeto Clients
La clase Hub incluye una propiedad Clients que contiene las siguientes propiedades para la comunicación entre servidor y 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 centro de conectividad |
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 siguientes métodos:
Método | Descripción |
---|---|
AllExcept | Llama a un método en todos los clientes conectados, excepto para 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 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 centro de conectividad |
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 método SendAsync
. El método SendAsync
recibe el nombre del método de cliente a llamar y cualquier parámetro.
Envío de mensajes a clientes
Para hacer llamadas a clientes específicos, use las propiedades del objeto Clients
. En el ejemplo siguiente, hay tres métodos de centro de conectividad:
SendMessage
envía un mensaje a todos los clientes conectados medianteClients.All
.SendMessageToCaller
devuelve un mensaje al autor de la llamada medianteClients.Caller
.SendMessageToGroup
envía un mensaje a todos los clientes del 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);
Concentradores fuertemente tipados
Un inconveniente de usar SendAsync
es que se basa en una cadena para especificar el método de cliente al que se va a llamar. Esto deja el código abierto a errores de runtime si el nombre del método está mal escrito o falta en el cliente.
Una alternativa a usar SendAsync
es tipar fuertemente la clase Hub con Hub<T>. En el siguiente ejemplo, el método cliente ChatHub
se ha extraído en una interfaz llamada IChatClient
:
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
Esta interfaz se puede usar para refactorizar el ejemplo anterior ChatHub
para que esté fuertemente 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);
}
El uso de Hub<IChatClient>
habilita la comprobación en tiempo de compilación de los métodos del cliente. Esto evita problemas causados por el uso de cadenas, ya que Hub<T>
solo puede proporcionar acceso a los métodos definidos en la interfaz. El uso de un Hub<T>
fuertemente tipado deshabilita la capacidad de usar SendAsync
.
Nota:
El sufijo Async
no se elimina de los nombres de los métodos. A menos que se defina un método de cliente con .on('MyMethodAsync')
, no use MyMethodAsync
como nombre.
Cambio del nombre de un método de centro de conectividad
De manera predeterminada, el nombre del método de un centro de conectividad del servidor es el nombre del método .NET. Para cambiar este comportamiento predeterminado para un método específico, use el atributo HubMethodName. El cliente debe usar este nombre en lugar del nombre del método de .NET al invocar el método :
[HubMethodName("SendMessageToUser")]
public async Task DirectMessage(string user, string message)
=> await Clients.User(user).SendAsync("ReceiveMessage", user, message);
Control de eventos de una conexión
La API de centros de conectividad de SignalR proporciona los métodos virtuales OnConnectedAsync y OnDisconnectedAsync para administrar y realizar un seguimiento de las conexiones. Sobrescriba el método virtual OnConnectedAsync
para realizar acciones cuando un cliente se conecta al centro de conectividad, como añadirlo a un grupo:
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
Sobrescriba el método virtual OnDisconnectedAsync
para realizar acciones cuando un cliente se desconecta. Si el cliente se desconecta intencionadamente, por ejemplo llamando a connection.stop()
, el parámetro exception
se establece en null
. Sin embargo, si el cliente se desconecta debido a un error, como un fallo de red, el parámetro exception
contiene una excepción que describe el fallo:
public override async Task OnDisconnectedAsync(Exception? exception)
{
await base.OnDisconnectedAsync(exception);
}
No es necesario llamar a RemoveFromGroupAsync en OnDisconnectedAsync, se hace automáticamente.
Control de errores
Las excepciones iniciadas en los métodos concentradores se envían al cliente que invocó el método . En el cliente de JavaScript, el método invoke
devuelve un Promise
de JavaScript. Los clientes pueden adjuntar un controlador de catch
a la promesa devuelta o usar try
/catch
con async
/await
para controlar excepciones:
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
Las conexiones no se cierran cuando un centro produce una excepción. De forma predeterminada, SignalR devuelve un mensaje de error genérico al cliente, como se muestra en el ejemplo siguiente:
Microsoft.AspNetCore.SignalR.HubException: An unexpected error occurred invoking 'SendMessage' 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 base de datos. SignalR no expone estos mensajes de error detallados de forma predeterminada como medida de seguridad. Para más información sobre por qué se suprimen los detalles de las excepciones, consulte Consideraciones de seguridad en ASP.NET Core SignalR.
Si se debe propagar una condición excepcional al cliente, use la clase HubException. Si se produce un HubException
en un método de centro de conectividad, SignalRenvía el mensaje de excepción completo al cliente, sin modificar:
public Task ThrowException()
=> throw new HubException("This error will be sent to the client!");
Nota
SignalR solo envía la propiedad Message
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.
Recursos adicionales
Por Rachel Appel y Kevin Griffin
Vea o descargue el código de ejemplo (cómo descargarlo)
Qué es una centro de conectividad de SignalR
La API de centros de conectividad SignalR permite a los clientes conectados llamar a métodos del servidor, lo que facilita la comunicación en tiempo real. El servidor define los métodos a los que llama el cliente y el cliente define los métodos a los que llama el servidor. SignalR también permite la comunicación indirecta de cliente a cliente, siempre mediada por el centro de SignalR, lo que permite enviar mensajes entre clientes individuales, grupos o a todos los clientes conectados. SignalR se encarga de todo lo necesario para hacer posible la comunicación de cliente a servidor y servidor a cliente en tiempo real.
Configuración de centros de conectividad de SignalR
El middleware de SignalR requiere algunos servicios, que se configuran mediante una llamada a AddSignalR:
services.AddSignalR();
Al añadir funcionalidad de SignalR a una aplicación de ASP.NET Core, configure rutas de SignalR llamando a MapHub en la devolución de llamada UseEndpoints del método Startup.Configure
:
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/chathub");
});
Nota:
Los ensamblados del lado servidor de SignalR de ASP.NET Core ahora están instalados con el SDK de .NET Core. Consulte ensamblados de SignalR en el marco compartido para más información.
Creación y uso de centros de conectividad
Crea un centro de conectividad declarando una clase que hereda de Hub, y agregándole métodos públicos. 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 y matrices complejos en los parámetros y los valores devueltos.
Nota:
Los centros de conectividad son transitorios:
- No almacene el estado en una propiedad de la clase de centro de conectividad. Cada llamada al método de centro de conectividad se ejecuta en una nueva instancia del centro de conectividad.
- No cree una instancia de un centro directamente a través de la inserción de dependencias. Para enviar mensajes a un cliente desde otro lugar de la aplicación, use
IHubContext
. - Use
await
cuando llame a métodos asíncronos que dependan de que el centro de conectividad permanezca activo. Por ejemplo, un método comoClients.All.SendAsync(...)
puede fallar si se llama sinawait
y el método del centro de conectividad termina antes de queSendAsync
termine.
Objeto Context
La clase Hub tiene una propiedad Context que contiene las siguientes propiedades con información sobre la 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 manera predeterminada, SignalR usa el ClaimTypes.NameIdentifier del ClaimsPrincipal asociado a la conexión como identificador de 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 centro de conectividad. |
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 con detalle. |
ConnectionAborted | Obtiene un CancellationToken que notifica cuando se anula la conexión. |
Hub.Context también contiene los siguientes métodos:
Método | Descripción |
---|---|
GetHttpContext | Devuelve HttpContext para la conexión, o null si la conexión no está asociada a una solicitud 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 Hub tiene una propiedad Clients que contiene las siguientes propiedades para la comunicación entre servidor y 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 centro de conectividad |
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 siguientes métodos:
Método | Descripción |
---|---|
AllExcept | Llama a un método en todos los clientes conectados, excepto para 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 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 centro de conectividad |
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 método SendAsync
. El método SendAsync
permite proporcionar el nombre y los parámetros del método cliente al que se va a llamar.
Envío de mensajes a clientes
Para hacer llamadas a clientes específicos, use las propiedades del objeto Clients
. En el ejemplo siguiente, hay tres métodos de centro de conectividad:
SendMessage
envía un mensaje a todos los clientes conectados medianteClients.All
.SendMessageToCaller
devuelve un mensaje al autor de la llamada medianteClients.Caller
.SendMessageToGroup
envía un mensaje a todos los clientes del 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);
}
Concentradores fuertemente tipados
Un inconveniente de usar SendAsync
es que se basa en una cadena mágica para especificar el método de cliente al que se va a llamar. Esto deja el código abierto a errores de runtime si el nombre del método está mal escrito o falta en el cliente.
Una alternativa a usar SendAsync
es tipar fuertemente el Hub con Hub<T>. En el siguiente ejemplo, los métodos del cliente ChatHub
se han extraído en una interfaz llamada IChatClient
.
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
Esta interfaz se puede usar para refactorizar el ejemplo anterior ChatHub
:
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 uso de Hub<IChatClient>
habilita la comprobación en tiempo de compilación de los métodos del cliente. Esto evita problemas causados por el uso de cadenas mágicas, ya que Hub<T>
solo puede proporcionar acceso a los métodos definidos en la interfaz.
El uso de un Hub<T>
fuertemente tipado 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 un Task
. Como se trata de una interfaz, no use la palabra clave async
. Por ejemplo:
public interface IClient
{
Task ClientMethod();
}
Nota
El sufijo Async
no se elimina del nombre del método. A menos que el método de cliente se defina con .on('MyMethodAsync')
, no debe usar MyMethodAsync
como nombre.
Cambio del nombre de un método de centro de conectividad
De manera predeterminada, el nombre del método de un centro de conectividad del servidor 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 de una conexión
La API de centros de conectividad de SignalR proporciona los métodos virtuales OnConnectedAsync y OnDisconnectedAsync para administrar y realizar un seguimiento de las conexiones. Sobrescriba el método virtual OnConnectedAsync
para realizar acciones cuando un cliente se conecta al centro de conectividad, como añadirlo a un grupo:
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
Sobrescriba el método virtual OnDisconnectedAsync
para realizar acciones cuando un cliente se desconecta. Si el cliente se desconecta intencionadamente (llamando a connection.stop()
, por ejemplo), el parámetro exception
será null
. Sin embargo, si el cliente está desconectado debido a un error (por ejemplo, un error de red), el parámetro exception
contendrá una excepción que describe el error:
public override async Task OnDisconnectedAsync(Exception exception)
{
await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", "I", "disconnect");
await base.OnDisconnectedAsync(exception);
}
No es necesario llamar a RemoveFromGroupAsync en OnDisconnectedAsync, se hace automáticamente.
Advertencia
Advertencia de seguridad: Exponer el ConnectionId
puede provocar suplantación malintencionada si la versión del cliente o el servidor de SignalR es ASP.NET Core 2.2 o versiones anteriores.
Control de errores
Las excepciones producidas en los métodos de centro de conectividad se envían al cliente que invocó el método. En el cliente de JavaScript, el método invoke
devuelve un Promise
de JavaScript. Cuando el cliente recibe un error con un controlador adjunto a la promesa usando catch
, se invoca y se pasa como un objeto de JavaScript Error
:
connection.invoke("SendMessage", user, message).catch(err => console.error(err));
Si su centro de conectividad 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 base de datos. SignalR no expone estos mensajes de error detallados de forma predeterminada como medida de seguridad. Para más información sobre por qué se suprimen los detalles de las excepciones, consulte Consideraciones de seguridad en ASP.NET Core SignalR.
Si tiene una condición excepcional que quiere propagar al cliente, puede usar la clase HubException. Si inicia un HubException
desde el método de centro de conectividad, SignalRenviará el mensaje completo al cliente, sin modificar:
public Task ThrowException()
{
throw new HubException("This error will be sent to the client!");
}
Nota
SignalR solo envía la propiedad Message
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.
Recursos adicionales
Por Rachel Appel y Kevin Griffin
Vea o descargue el código de ejemplo (cómo descargarlo)
Qué es una centro de conectividad de SignalR
La API de centros de conectividad SignalR permite a los clientes conectados llamar a métodos del servidor, lo que facilita la comunicación en tiempo real. El servidor define los métodos a los que llama el cliente y el cliente define los métodos a los que llama el servidor. SignalR también permite la comunicación indirecta de cliente a cliente, siempre mediada por el centro de SignalR, lo que permite enviar mensajes entre clientes individuales, grupos o a todos los clientes conectados. SignalR se encarga de todo lo necesario para hacer posible la comunicación de cliente a servidor y servidor a cliente en tiempo real.
Configuración de centros de conectividad de SignalR
El middleware de SignalR requiere algunos servicios, que se configuran mediante una llamada a AddSignalR:
services.AddSignalR();
Al añadir funcionalidad de SignalR a una aplicación de ASP.NET Core, configure rutas de SignalR llamando a UseSignalR en el método Startup.Configure
:
app.UseSignalR(route =>
{
route.MapHub<ChatHub>("/chathub");
});
Creación y uso de centros de conectividad
Crea un centro de conectividad declarando una clase que hereda de Hub, y agregándole métodos públicos. 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 y matrices complejos en los parámetros y los valores devueltos.
Nota:
Los centros de conectividad son transitorios:
- No almacene el estado en una propiedad de la clase de centro de conectividad. Cada llamada al método de centro de conectividad se ejecuta en una nueva instancia del centro de conectividad.
- No cree una instancia de un centro directamente a través de la inserción de dependencias. Para enviar mensajes a un cliente desde otro lugar de la aplicación, use
IHubContext
. - Use
await
cuando llame a métodos asíncronos que dependan de que el centro de conectividad permanezca activo. Por ejemplo, un método comoClients.All.SendAsync(...)
puede fallar si se llama sinawait
y el método del centro de conectividad termina antes de queSendAsync
termine.
Objeto Context
La clase Hub tiene una propiedad Context que contiene las siguientes propiedades con información sobre la 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 manera predeterminada, SignalR usa el ClaimTypes.NameIdentifier del ClaimsPrincipal asociado a la conexión como identificador de 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 centro de conectividad. |
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 con detalle. |
ConnectionAborted | Obtiene un CancellationToken que notifica cuando se anula la conexión. |
Hub.Context también contiene los siguientes métodos:
Método | Descripción |
---|---|
GetHttpContext | Devuelve HttpContext para la conexión, o null si la conexión no está asociada a una solicitud 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 Hub tiene una propiedad Clients que contiene las siguientes propiedades para la comunicación entre servidor y 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 centro de conectividad |
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 siguientes métodos:
Método | Descripción |
---|---|
AllExcept | Llama a un método en todos los clientes conectados, excepto para 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 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 centro de conectividad |
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 método SendAsync
. El método SendAsync
permite proporcionar el nombre y los parámetros del método cliente al que se va a llamar.
Envío de mensajes a clientes
Para hacer llamadas a clientes específicos, use las propiedades del objeto Clients
. En el ejemplo siguiente, hay tres métodos de centro de conectividad:
SendMessage
envía un mensaje a todos los clientes conectados medianteClients.All
.SendMessageToCaller
devuelve un mensaje al autor de la llamada medianteClients.Caller
.SendMessageToGroup
envía un mensaje a todos los clientes del 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);
}
Concentradores fuertemente tipados
Un inconveniente de usar SendAsync
es que se basa en una cadena mágica para especificar el método de cliente al que se va a llamar. Esto deja el código abierto a errores de runtime si el nombre del método está mal escrito o falta en el cliente.
Una alternativa a usar SendAsync
es tipar fuertemente el Hub con Hub<T>. En el siguiente ejemplo, los métodos del cliente ChatHub
se han extraído en una interfaz llamada IChatClient
.
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
}
Esta interfaz se puede usar para refactorizar el ejemplo anterior ChatHub
:
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 uso de Hub<IChatClient>
habilita la comprobación en tiempo de compilación de los métodos del cliente. Esto evita problemas causados por el uso de cadenas mágicas, ya que Hub<T>
solo puede proporcionar acceso a los métodos definidos en la interfaz.
El uso de un Hub<T>
fuertemente tipado 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 un Task
. Como se trata de una interfaz, no use la palabra clave async
. Por ejemplo:
public interface IClient
{
Task ClientMethod();
}
Nota
El sufijo Async
no se elimina del nombre del método. A menos que el método de cliente se defina con .on('MyMethodAsync')
, no debe usar MyMethodAsync
como nombre.
Cambio del nombre de un método de centro de conectividad
De manera predeterminada, el nombre del método de un centro de conectividad del servidor 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 de una conexión
La API de centros de conectividad de SignalR proporciona los métodos virtuales OnConnectedAsync y OnDisconnectedAsync para administrar y realizar un seguimiento de las conexiones. Sobrescriba el método virtual OnConnectedAsync
para realizar acciones cuando un cliente se conecta al centro de conectividad, como añadirlo a un grupo:
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
Sobrescriba el método virtual OnDisconnectedAsync
para realizar acciones cuando un cliente se desconecta. Si el cliente se desconecta intencionadamente (llamando a connection.stop()
, por ejemplo), el parámetro exception
será null
. Sin embargo, si el cliente está desconectado debido a un error (por ejemplo, un error de red), el parámetro exception
contendrá una excepción que describe el error:
public override async Task OnDisconnectedAsync(Exception exception)
{
await Clients.Group("SignalR Users").SendAsync("ReceiveMessage", "I", "disconnect");
await base.OnDisconnectedAsync(exception);
}
No es necesario llamar a RemoveFromGroupAsync en OnDisconnectedAsync, se hace automáticamente.
Advertencia
Advertencia de seguridad: Exponer el ConnectionId
puede provocar suplantación malintencionada si la versión del cliente o el servidor de SignalR es ASP.NET Core 2.2 o versiones anteriores.
Control de errores
Las excepciones producidas en los métodos de centro de conectividad se envían al cliente que invocó el método. En el cliente de JavaScript, el método invoke
devuelve un Promise
de JavaScript. Cuando el cliente recibe un error con un controlador adjunto a la promesa usando catch
, se invoca y se pasa como un objeto de JavaScript Error
:
connection.invoke("SendMessage", user, message).catch(err => console.error(err));
Si su centro de conectividad 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 base de datos. SignalR no expone estos mensajes de error detallados de forma predeterminada como medida de seguridad. Para más información sobre por qué se suprimen los detalles de las excepciones, consulte Consideraciones de seguridad en ASP.NET Core SignalR.
Si tiene una condición excepcional que quiere propagar al cliente, puede usar la clase HubException. Si inicia un HubException
desde el método de centro de conectividad, SignalRenviará el mensaje completo al cliente, sin modificar:
public Task ThrowException()
{
throw new HubException("This error will be sent to the client!");
}
Nota
SignalR solo envía la propiedad Message
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.