Aplicación web en tiempo real con ASP.NET Core SignalR
En este tema, veremos cómo crear aplicaciones para la transmisión y la recepción de datos con ASP.NET Core SignalR.
Lo que necesitarás:
- Un conocimiento básico de ASP.NET Core y Angular.
- .NET Core 3.1 instalado e IDE como Visual Studio.
Lo que sabrá al final de este artículo:
- Cómo agregar y usar SignalR.
- Cómo abrir la conexión del Cliente y utilizar el concepto de invocación de método para transmitir datos por Cliente.
- Cómo consumir el servicio SignalR con la aplicación Angular mediante Observables.
SignalR aprovecha varios transportes y selecciona automáticamente el mejor transporte disponible según las capacidades del cliente y del servidor: WebSockets, Server Send Events o Long-polling.
Cuando hablamos en términos de WebSockets (excluyendo SSE y Long-polling de la ecuación) cuando el cliente está conectado en tiempo real al servidor, cada vez que sucede algo, el servidor sabrá enviar un mensaje a través de ese WebSocket al cliente. Con clientes y servidores de la vieja escuela, se utilizaría el transporte de sondeo largo.
Así es como SignalR maneja clientes y servidores modernos, utiliza WebSockets internamente cuando está disponible y recurre elegantemente a otras técnicas y tecnologías cuando no lo está:

Es como un apretón de manos, el Cliente y el Servidor acuerdan qué usar y lo usan. Esto se llama proceso de negociación.

SignalR Example
El propósito de esta demostración es mostrar un tablero de pantalla financiera con un flujo de datos en tiempo real utilizando ASP.NET Core SignalR.
SignalR Server Configuration
Create ASP.NET Core App
Veamos cómo configurar la aplicación SignalR ASP.NET Core. En Visual Studio, desde Archivo >> Nuevo proyecto, elija ASP.NET Core aplicación web y siga la configuración. No dude en seguir el tutorial de documentación oficial de Microsoft si experimenta alguna dificultad de configuración.

SignalR Config Setup
Agregue el siguiente código al archivo Startup.cs:
- Endpoint part of the
Configuremethod.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<StreamHub>("/streamHub");
});
- Add SignalR usage to the
ConfigureServicesmethod.
services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
});
Los cambios anteriores agregan SignalR al sistema de enrutamiento e inserción de dependencias ASP.NET Core.
Ahora, configuremos una configuración básica adicional. Abra el archivo properties/launchSettings.json y modifíquelo en consecuencia:
"profiles": {
"WebAPI": {
"commandName": "Project",
"launchBrowser": false,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
Our server-side project will run on localhost:5001 and the client side will run on localhost:4200, so in order to establish communication between those two, we need to enable CORS. Let’s open the Startup.cs class and modify it:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.WithOrigins("http://localhost:4200"));
});
...
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseCors("CorsPolicy");
...
Si experimenta un problema específico al habilitar el uso compartido de recursos entre orígenes, consulte el tema oficial de Microsoft.
SignalR Hub Setup
Comencemos explicando qué es un concentrador SignalR. La API de SignalR Hub le permite llamar a métodos en clientes conectados desde el servidor. En el código del servidor, usted define los métodos que llama el cliente. En SignalR existe este concepto llamado Invocación: en realidad, puedes llamar al centro desde el cliente con un método particular. En el código del cliente, usted define métodos que se llaman desde el servidor.
The actual hub lives on the server-side. Imagine you have Clients and the Hub is between all of them. You can say something to all the Clients with Clients.All.doWork() by invoking a method on the hub. This will goes to all connected clients. Also, you can communicate with only one client, which is the Caller, because he is the caller of that particular method.

Hemos creado una clase StreamHub que hereda la clase base Hub, que es responsable de administrar conexiones, grupos y mensajería. Es bueno tener en cuenta que la clase Hub no tiene estado y cada nueva invocación de un determinado método se realiza en una nueva instancia de esta clase. Es inútil guardar el estado en las propiedades de la instancia; más bien sugerimos usar propiedades estáticas; en nuestro caso usamos una colección estática de pares clave-valor para almacenar datos para cada cliente conectado.
Otras propiedades útiles de esta clase son Clientes, Contexto y Grupos. Pueden ayudarle a gestionar cierto comportamiento basándose en el ConnectionID único. Además, esta clase le proporciona los siguientes métodos útiles:
- OnConnectedAsync(): se llama cuando se establece una nueva conexión con el concentrador.
- OnDisconnectedAsync (Excepción): se llama cuando finaliza una conexión con el concentrador.
Nos permiten realizar cualquier lógica adicional cuando se establece o cierra una conexión. En nuestra aplicación, también agregamos el método UpdateParameters que obtiene una ID de conexión de contexto y la usa para enviar datos en un intervalo determinado. Como puede ver, nos comunicamos a través de un ConnectionID único que evita una intervención de transmisión por parte de otros Clientes.
public async void UpdateParameters(int interval, int volume, bool live = false, bool updateAll = true)
{
...
var connection = Context.ConnectionId;
var clients = Clients;
...
if (!clientConnections.ContainsKey(connection))
{
clientConnections.Add(connection, new TimerManager(async() =>
{
...
await Send(newDataArray, client, connection);
}, interval));
} else
{
clientConnections[connection].Stop();
clientConnections[connection] = new TimerManager(async () =>
{
var client = clients.Client(connection);
..
await Send(newDataArray, client, connection);
}, interval);
}
...
}
When the data is ready we transfer it by emitting a transferdata event with the help of SendAsync Method.
public async Task Send(FinancialData[] array, IClientProxy client, string connection)
{
await client.SendAsync("transferdata", array);
}
...
// Called when a connection with the hub is terminated
public override Task OnDisconnectedAsync(Exception exception)
{
StopTimer();
clientConnections.Remove(Context.ConnectionId);
return base.OnDisconnectedAsync(exception);
}
Nuestra aplicación cliente estaría escuchando los eventos registrados:
private registerSignalEvents() {
this.hubConnection.onclose(() => {
this.hasRemoteConnection = false;
});
this.hubConnection.on('transferdata', (data) => {
this.data.next(data);
})
}
El repositorio público de GitHub de la aplicación ASP.NET Core se puede encontrar aquí.
Create SignalR Client Library
We will create an Angular project in order to consume the SignalR service. The GitHub repository with the actual application can be found in the igniteui-angular-samples repository.
First, start by installing SignalR:
npm install @microsoft/signalr
Tenga en cuenta que enviaremos la solicitud HTTP a nuestro servidor, por lo que también necesitamos HttpClientModule.
A continuación encontrará el archivo signal-r.service.ts que maneja el generador de conexiones del concentrador.
export class SignalRService implements OnDestroy {
public data: BehaviorSubject<any[]>;
public hasRemoteConnection: boolean;
private hubConnection: signalR.HubConnection;
...
constructor(private zone: NgZone, private http: HttpClient) {
this.data = new BehaviorSubject([]);
}
...
// Start Hub Connection and Register events
public startConnection = (interval = 500, volume = 1000, live = false, updateAll = true) => {
this.hubConnection = new signalR.HubConnectionBuilder()
.configureLogging(signalR.LogLevel.Trace)
.withUrl('https://es.infragistics.com/angular-apis/webapi/streamHub')
.build();
this.hubConnection
.start()
.then(() => {
...
this.registerSignalEvents();
this.broadcastParams(interval, volume, live, updateAll);
})
.catch(() => { ... });
}
// Change the broadcast parameters like frequency and data volume
public broadcastParams = (frequency, volume, live, updateAll = true) => {
this.hubConnection.invoke('updateparameters', frequency, volume, live, updateAll)
.then(() => console.log('requestLiveData', volume))
.catch(err => {
console.error(err);
});
}
// Register events
private registerSignalEvents() {
this.hubConnection.onclose(() => {
this.hasRemoteConnection = false;
});
this.hubConnection.on('transferdata', (data) => {
this.data.next(data);
});
}
...
In your app.component add use the newly created startConnection method
constructor(public dataService: SignalRService) {}
public ngOnInit() {
this.dataService.startConnection(this.frequency, this.dataVolume, true, false);
}
...
Grid Data Binding
As we have seen so far in our client code we set up a listener for transferdata event, which receives as an argument the updated data array. To pass the newly received data to our grid we use an observable. To set that, we need to bind the grid's data source to the data observable like so:
<igx-grid [data]='data | async'> ... </igx-grid>
Every time when new data is received from the server to the client we call the next() method of the data observable.
this.hubConnection.on('transferdata', (data) => {
this.data.next(data);
})
Topic Takeaways
Si no desea actualizar la aplicación, sino simplemente ver cuándo se actualizan los datos, debe considerar ASP.NET Core SignalR. Definitivamente recomiendo optar por la transmisión de contenido cuando crea que sus datos son grandes, o si desea una experiencia de usuario fluida sin bloquear el cliente mostrando giros interminables.
Usar la comunicación de SignalR Hub es fácil e intuitivo y con la ayuda de Angular Observables, puedes crear una aplicación poderosa que utilice la transmisión de datos con WebSockets.