Aplicativo Web em tempo real com ASP.NET Core SignalR
Neste tópico, veremos como criar aplicativos para streaming e recebimento de dados com ASP.NET Core SignalR.
O que você precisará:
- Um conhecimento básico de ASP.NET Core e Angular.
- .NET Core 3.1 instalado e IDE, como Visual Studio.
O que você saberá até o final deste artigo:
- Como adicionar e usar o SignalR.
- Como abrir a conexão do cliente e usar o conceito de invocação do método para transmitir dados por cliente.
- Como consumir o serviço SignalR com Angular aplicativo usando Observáveis.
O SignalR aproveita vários transportes e seleciona automaticamente o melhor transporte disponível, considerando os recursos do cliente e do servidor –WebSockets, Eventos de Envio do Servidor ou Sondagem Longa.
Quando falamos em termos de WebSockets (Colocando SSE e Long-polling fora da equação) quando o cliente está conectado em tempo real ao servidor, sempre que algo acontecer, o servidor saberá enviar uma mensagem por esse WebSocket de volta ao cliente. Com clientes e servidores antigos, o transporte de sondagem longa seria usado.
É assim que o SignalR lida com clientes e servidores modernos, ele usa WebSockets sob o capô quando disponível e normalmente recorre a outras técnicas e tecnologias quando não é:
É como um aperto de mão, o cliente e o servidor concordam sobre o que usar e eles usam. Isso é chamado de negociação de processo.
SignalR Example
O objetivo desta demonstração é mostrar um quadro de tela financeira com um fluxo de dados em tempo real usando ASP.NET Core SignalR.
SignalR Server Configuration
Create ASP.NET Core App
Vamos ver como configurar o aplicativo ASP.NET Core SignalR. No projeto Visual Studio from File >> New, escolha ASP.NET Core Web Application e siga a configuração. Sinta-se à vontade para seguir o tutorial oficial da documentação da Microsoft se tiver alguma dificuldade de configuração.
SignalR Config Setup
Adicione o seguinte código ao arquivo Startup.cs:
- Parte do ponto final do
Configure
método.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<StreamHub>("/streamHub");
});
- Adicione o uso do
ConfigureServices
SignalR ao método.
services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
});
As alterações acima estão adicionando o SignalR ao sistema de roteamento e injeção de dependência ASP.NET Core.
Agora, vamos definir a configuração básica adicional. Abra o arquivo properties/launchSettings.json e modifique-o de acordo:
"profiles": {
"WebAPI": {
"commandName": "Project",
"launchBrowser": false,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
Nosso projeto do lado do servidor será executado e localhost:5001
o lado do cliente será executado localhost:4200
, portanto, para estabelecer a comunicação entre os dois, precisamos habilitar o CORS. Vamos abrir a classe Startup.cs e modificá-la:
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");
...
Se você tiver um problema específico ao habilitar o compartilhamento de recursos entre origens, confira o tópico oficial da Microsoft.
SignalR Hub Setup
Vamos começar explicando o que é um hub do SignalR? A API do Hub do SignalR permite que você chame métodos em clientes conectados do servidor. No código do servidor, você define métodos que são chamados pelo cliente. No SignalR, há esse conceito chamado Invocação– você pode realmente chamar o hub do cliente com um método específico. No código do cliente, você define métodos que são chamados do servidor.
O hub real reside no lado do servidor. Imagine que você tem clientes e o Hub está entre todos eles. Você pode dizer algo a todos os clientes invocando Clients.All.doWork()
um método no hub. Isso vai para todos os clientes conectados. Além disso, você pode se comunicar com apenas um cliente, que é o chamador, porque ele é o chamador desse método específico.
Criamos uma classe StreamHub que herda a classe base Hub, que é responsável por gerenciar conexões, grupos e mensagens. É bom ter em mente que a classe Hub é stateless e cada nova invocação de um determinado método está em uma nova instância dessa classe. É inútil salvar o estado nas propriedades da instância, em vez disso, sugerimos o uso de propriedades estáticas, no nosso caso, usamos a coleção estática de pares chave-valor para armazenar dados para cada cliente conectado.
Outras propriedades úteis dessa classe são Clients, Context e Groups. Eles podem ajudá-lo a gerenciar determinado comportamento com base no ConnectionID exclusivo. Além disso, essa classe fornece os seguintes métodos úteis:
- OnConnectedAsync() - Chamado quando uma nova conexão é estabelecida com o hub.
- OnDisconnectedAsync(Exception) - Chamado quando uma conexão com o hub é encerrada.
Eles nos permitem executar qualquer lógica adicional quando uma conexão é estabelecida ou fechada. Em nosso aplicativo, também adicionamos o método UpdateParameters que obtém uma ID de conexão de contexto e a usa para enviar dados de volta em um determinado intervalo. Como você pode ver, nos comunicamos por meio de um ConnectionID exclusivo que impede uma intervenção de streaming de outros 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);
}
...
}
Quando os dados estão prontos, nós os transferimos emitindo um transferdata
evento com a ajuda do SendAsync
Método.
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);
}
Nosso aplicativo cliente estaria ouvindo os eventos registrados:
private registerSignalEvents() {
this.hubConnection.onclose(() => {
this.hasRemoteConnection = false;
});
this.hubConnection.on('transferdata', (data) => {
this.data.next(data);
})
}
O repositório público do GitHub do aplicativo ASP.NET Core pode ser encontrado aqui.
Create SignalR Client Library
Criaremos um projeto Angular para consumir o serviço SignalR. O repositório do Github com o aplicativo real pode ser encontrado aqui.
First, start by installing SignalR:
npm install @microsoft/signalr
Lembre-se de que enviaremos a solicitação HTTP para o nosso servidor, portanto, também precisamos de HttpClientModule.
Abaixo, você encontrará o arquivo signal-r.service.ts que lida com o construtor de conexões do hub.
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://pt-br.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);
});
}
...
Em seu app.component add, use o método recém-criado startConnection
constructor(public dataService: SignalRService) {}
public ngOnInit() {
this.dataService.startConnection(this.frequency, this.dataVolume, true, false);
}
...
Grid Data Binding
Como vimos até agora em nosso código de cliente, configuramos um ouvinte para transferdata
event, que recebe como argumento o array de dados atualizado. Para passar os dados recém-recebidos para nossa grade, usamos um observável. Para definir isso, precisamos vincular a fonte de dados da grade aos dados observáveis da seguinte forma:
<igx-grid [data]='data | async'> ... </igx-grid>
Toda vez que novos dados são recebidos do servidor para o cliente, chamamos o next()
método dos dados observáveis.
this.hubConnection.on('transferdata', (data) => {
this.data.next(data);
})
Topic Takeaways
Se você não quiser atualizar seu aplicativo, em vez de apenas ver quando os dados são atualizados, considere ASP.NET Core SignalR. Definitivamente, recomendo usar conteúdo de streaming quando você achar que seus dados são grandes ou se quiser uma experiência de usuário tranquila sem bloquear o cliente mostrando spinners infinitos.
Usar a comunicação do Hub do SignalR é fácil e intuitivo e, com a ajuda do Angular Observables, você pode criar um aplicativo poderoso que usa streaming de dados com WebSockets.