Aplicativos modernos

Use o SignalR para criar aplicativos modernos

Rachel Appel

Baixar o código de exemplo

Rachel AppelCom o acesso generalizado à Internet de banda larga e dispositivos sem fio, há uma demanda considerável de aplicativos em tempo real. Sites populares como Facebook e Twitter, jogos para vários jogadores e aplicativos de negócios colaborativos têm o melhor UX quando estão com aplicativos ao vivo, em tempo real. Muitos outros tipos de aplicativos são excelentes candidatos para experiências em tempo real, tais como aplicativos de ações e finanças, leilões, painéis de vendas, comércio eletrônico e aplicativos educacionais. Mesmo sites e aplicativos onde os dados ao vivo não são uma necessidade podem se beneficiar das comunicações full duplex em tempo real com o SignalR.

O que é o SignalR e por que devo usá-lo?

O SignalR é um conjunto de bibliotecas de cliente e servidor que facilita comunicações bidirecionais simples em tempo real entre o servidor e o cliente. Não apenas o cliente inicia o contato com o servidor, como é o caso no desenvolvimento para a Web, mas o servidor também pode entrar em contato com o cliente. Aqueles que não são apenas respostas HTTP simples, também. Essas são chamadas de método reais do servidor para o cliente, como a tecnologia de push. Os clientes ainda podem entrar em contato com outros clientes através do componente do lado do servidor SignalR. Tudo isso é possível porque o SignalR cria uma conexão persistente entre o servidor e o cliente.

Todo mundo quer criar software moderno, e não existe nada mais moderno do que comunicações full duplex. Há várias razões para usar o SignalR. Sua facilidade de uso para criar sites e aplicativos é uma boa razão. Outra razão é se você precisa de comunicação ao vivo em seu software. Nesses casos, o SignalR é o caminho a percorrer. Você pode fazer isso sozinho usando várias técnicas, como WebSockets ou sondagem AJAX. No entanto, você teria que regravar toda a base que a equipe SignalR já fez. Essa base é bastante ampla e inclui vários recursos principais:

  • Negociação de transporte: O SignalR detecta o melhor transporte para chegar tão perto de comunicações em tempo real quanto for possível. Ele usa WebSockets por padrão, já que é a maneira mais rápida e moderna de criar aplicativos Web em tempo real. O gerenciamento automático do transporte está por trás da ideia de comunicação em tempo real no SignalR. Ele negocia um transporte para cada cliente conforme ele se conecta.
  • Host do servidor SignalR: Você pode escolher entre as plataformas de auto-hospedagem leves em qualquer lugar, inclusive plataformas que não são da Microsoft, ou prender o SignalR no pipeline do IIS.
  • Bibliotecas do lado do cliente: Incluindo bibliotecas JavaScript, Microsoft.NET Framework e Windows Store.
  • Proxy do JavaScript: Isso fornece uma maneira de chamar métodos em locais remotos no JavaScript, enquanto desenvolve como se todo o código estivesse sendo executado no mesmo processo, na mesma máquina.
  • Segurança: O SignalR se conecta aos modelos de segurança ASP.NET existentes e suporta muitos vários de segurança de terceiros populares, como o Microsoft Live, OpenAuth, Google, Facebook e Twitter.

Os desenvolvedores da Web geralmente escrevem o código de acordo com o modelo de solicitação/resposta de HTTP. Não há nada de intrinsecamente ruim com isso, mas falta o principal benefício do SignalR - uma conexão persistente entre o servidor e o cliente. Em HTTP, você faz um pedido, obtém uma resposta e você terminou. Em um cenário em tempo real, o pipeline permanece aberto entre o servidor e o cliente. Isso permite que você crie UXes mais ricos e melhores que se sentem vivos e conectados.

No SignalR, há duas camadas de abstração sobre os transportes de baixo nível - os hubs e as conexões persistentes. Este artigo abordará apenas hubs por razões de brevidade. Os hubs são o API de nível superior que é a parte "incrivelmente simples" do SignalR. As conexões persistentes demoram mais tempo e exigem mais esforço para codificar e o SignalR as usa como base para hubs. Você geralmente vai usar os hubs para a maioria de suas atividades, a menos que você tenha boas razões para fazer o contrário.

Introdução ao SignalR com Aplicativos do Windows

Como em muitas outras bibliotecas .NET, o SignalR é fornecido como um pacote NuGet. Você pode instalá-lo com o Gerenciador de Pacotes NuGet ou o Console do Gerenciador de Pacotes. Ambos são recursos do Visual Studio 2012 e Visual Studio 2013. Há vários pacotes SignalR diferentes disponíveis na Microsoft, incluindo:

  • Microsoft ASP.NET SignalR: O pacote base que instala os componentes principais e Web com o cliente JavaScript
  • Componentes principais do Microsoft ASP.NET SignalR: Hospedagem e bibliotecas principais
  • Microsoft ASP.NET SignalR System.Web: SignalR para ASP.NET
  • Microsoft ASP.NET SignalR JavaScript Client: Bibliotecas cliente do JavaScript para aplicativos HTML
  • Microsoft ASP.NET SignalR .NET Client: Bibliotecas cliente para outros aplicativos da plataforma Windows

Quando você instala o pacote Microsoft ASP.NET SignalR em qualquer um de seus projetos ASP.NET (Web Forms ou MVC), o SignalR instala as dependências em cada um dos pacotes listados, exceto o cliente .NET. O cliente .NET é para aplicativos do Windows 8 e Windows Phone XAML, Windows Presentation Foundation (WPF), Windows Forms e aplicativos de console. Há mais pacotes SignalR no Gerenciador de Pacotes NuGet da Microsoft e de terceiros. Eles abrangem praticamente tudo, inclusive auto-hospedagem, dimensionamento, injeção de dependência e suporte MongoDB.

Após a instalação, não há nenhuma configuração web.config para ajustar. No entanto, você deve adicionar um pequeno trecho de código de inicialização para indicar ao ASP.NET que você estará inserindo o SignalR em seu pipeline:

[assembly: OwinStartup(typeof(VoteR.Startup))]
public partial class Startup
{
  public void Configuration(IAppBuilder app)
  {
    app.MapSignalR();
  }
}

Você pode adicionar essa classe de inicialização a um arquivo .cs na pasta App_Start. Alguns modelos de projeto ASP.NET já incluem uma classe de inicialização para autenticação de formulários ASP.NET. Se esse for o caso, basta adicionar o método Configuração para essa classe no seu lugar. Uma vez feito isso, você pode passar para a gravação do código em tempo real.

Essa classe de inicialização é uma interface da Web aberta para inicialização do .NET (OWIN). Isso significa que ela adere às novas especificações OWIN. OWIN é um padrão muito parecido com as normas regidas pelo Worldwide Web Consortium (W3C). A Microsoft implementou o OWIN em um projeto chamado Katana. Esse é o mecanismo por trás do SignalR que trabalha em tandem com o IIS ou como uma auto-hospedagem para facilitar a comunicação bidirecional. Como um desenvolvedor SignalR usando hubs, você não precisa saber muito mais do que isso sobre OWIN ou Katana. O SignalR abstrai tudo o que se distancia para que você possa se concentrar em resolver seus problemas de negócios com SignalR.

O lado do servidor dos hubs do SignalR

Os hubs são o núcleo de comunicações do SignalR. Eles recebem as solicitações de entrada e enviam mensagens para os clientes, seja do próprio hub ou em nome de outro cliente. Você pode considerar um hub no SignalR como um hub e um problema.

O hub é apenas um gatekeeper para o sistema de mensagens. Enquanto eles estão no centro da ação, os hubs são apenas classes que herdam da classe Microsoft.AspNet.SignalR.Hub. A classe Hub, por sua vez, implementa a interface IHub do namespace Microsoft.AspNet.SignalR.Hubs. A interface IHub define três eventos: OnConnected, OnDisconnected e OnReconnected. Ele também define três propriedades: Clients, Context e Groups. Elas são tarefas comuns ou informações relacionadas a cada conexão em tempo real para o hub.

O cliente chama os métodos públicos no hub, o que significa que o código se parece com uma chamada do serviço da Web. No entanto, os hubs do SignalR podem iniciar o contato com os clientes que foram registrados com eles. Você normalmente não programará com este tipo de comportamento em mente, pois normalmente usaria o modelo de solicitação/resposta tradicional.

Isso pode acontecer por causa da propriedade Clients, que representa um conjunto de todos os clientes conectados. Através da propriedade Clients, você pode acessar um único cliente ou vários clientes, independentemente de sua plataforma. Por exemplo, um cliente iOS pode enviar uma mensagem para um cliente do Windows através do hub, porque o hub se comunica com o cliente Windows em nome do cliente iOS e vice-versa.

Para ver o hub em ação, eu examinarei um aplicativo de amostra chamado VoteR que exibe uma série de itens e permite que os usuários votem no seu favorito. No núcleo do aplicativo há uma classe VoteHub. Esse é o hub que conta os votos dos usuários que foram convertidos para cada item. Em seguida, ele notifica os clientes sobre os números atualizados. A Figura 1 mostra um exemplo da classe VoteHub.

Figura 1 A classe VoteHub conta os votos

public class VoteHub : Hub
{
  private static List<Item> VoteItems = new List<Item>();
  private static VoteRContext db = new VoteRContext();
  public void Vote(int id)
  {  
    var votes = AddVote(id);
    Clients.All.updateVoteResults(id, votes);
  }
  private static Item AddVote(int id) {
    var voteItem = VoteItems.Find(v => v.Id == id);       
    if (voteItem != null)
    {
      voteItem.Votes++;
      return voteItem;
    }
    else
    {
      var item = db.Items.Find(id);
      item.Votes++;
      VoteItems.Add(item);
      return item;  
    }       
  }
  public override Task OnConnected()
  {       
    Clients.Caller.joinVoting(VoteItems.ToList());
    return base.OnConnected();
  }
}

Os dois métodos para investigar na Figura 1 são os métodos Vote e Add-Vote. O método Vote é o que os clientes chamam (abordado na próxima seção). O aplicativo chama o método AddVote privado, que faz a contagem de votos reais. Ele faz isso verificando se os itens já estão na lista VoteItems. Se estiverem, ele os atualizará. Caso contrário, a primeira vez que o usuário votar neles, o AddVote acrescentará esse item. Uma lista estática <Vote> é uma maneira fácil de armazenar informações globais simples como essa sem um banco de dados.

O método Vote na Figura 1 contém uma linha de código interessante após sua chamada para o AddVote:

Clients.All.updateVoteResults(id, votes);

No SignalR, você usará a propriedade Clients para acessar e chamar o código no cliente. Na propriedade Clients, você pode destinar os clientes que deseja acessar. Às vezes são todos os clientes clientes, assim como quando ocorre uma nova votação. Às vezes é apenas um cliente, como quando um usuário se conecta pela primeira vez. Usar convenções de nomenclatura permite que o SignalR corresponda a chamada do servidor com o código do cliente a ser executado. Essa é a primeira vez na história do ASP.NET que você pode usar propriedades dinâmicas para chamar o código no cliente.

Como se pode imaginar, como o VoteHub precisar acompanhar votos, faz sentido que exista um evento como OnConnected. O evento OnConnected permite captar conexões novas recebidas. Um cenário provável é interceptar a ID da conexão através da propriedade ConnectionID do objeto Context.

No caso do aplicativo VoteR, na Figura 1 está circulando através de três itens no objeto <Item> da lista VoteHub e notificando o cliente sobre quantos votos cada item tem através da propriedade Clients.caller. Dessa forma, os clientes recentemente conectados imediatamente têm o número total de votos de cada item com a entrada na festa.

Há muitas outras formas de se comunicar entre o servidor e o cliente. A propriedade Clients da classe Hub expõe as várias maneiras diferentes que podemos acessar o código do cliente, conforme descrito na Figura 2.

Figura 2 Várias maneiras de se comunicar do servidor para o cliente

// Call method on all clients
Clients.All.clientSideMethod(args, args, ...);
// Call method on specific client       
Clients.Client(Context.ConnectionId). clientSideMethod(args, args, ...);
// Call a method on a list of specific connections
Clients.Clients(ConnectionId1, ConnectionId1, ...).clientSideMethod(args, args, ...);
// Call method on calling connection
Clients.Caller.clientSideMethod(args, args, ...);
// Call method on all clients but the caller
Clients.Others. clientSideMethod(args, args, ...);
// Call method on all in the group except a few select connections
Clients.AllExcept(connectionId1, connectionId2).clientSideMethod(args, args, ...);
// Call method on groups of connections
Clients.Group(groupName).clientSideMethod(args, args, ...);
// Call method on connected clients in a specified group
Clients.Groups(GroupIds).clientSideMethod(args, args, ...);
// Call method on other connected clients in a specified group
Clients.OthersInGroup(groupName).clientSideMethod(args, args, ...);

Na Figura 2, o clientSideMethod é definido nas chamadas do servidor do cliente. Na próxima seção, você aprenderá como definir esses métodos no cliente. Como você pode ver, a natureza dinâmica da propriedade Clients permite que você grave o código para uma ampla variedade de cenários de comunicação entre servidor e cliente.

O lado do cliente do SignalR: Clientes JavaScript e .NET

Você pode criar aplicativos em tempo real em qualquer plataforma com o SignalR. Inicialmente, você pode usar o cliente JavaScript SignalR para todas as coisas da Web e aplicativos do cliente HTML, incluindo aplicativos WinJS. HTML sem formatação e JavaScript são linguagens amplamente suportadas. Para os amigos do .NET, a Microsoft lançou um cliente .NET para Windows e aplicativos da área de trabalho. Como os componentes de núcleo, você instala o cliente JavaScript ou .NET pelo NuGet, dependendo do tipo do seu projeto. Como o JavaScript é apenas JavaScript, você pode baixar os scripts em github.com/SignalR/SignalR e adicionar marcas <script> à sua página, ao contrário de fazer referência aos arquivos .dll, como mostrado aqui:

<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.signalR-2.0.3.js"></script>
<script src="~/signalr/hubs"></script>

A ordem das referências de script é importante. Você deve carregar jQuery primeiro porque o SignalR depende dele. Em seguida está o cliente SignalR. O último é o proxy SignalR. O SignalR gera dinamicamente um proxy no tempo de execução e redistribui em /signalr/hubs. Esse proxy é o que permite gravar o código no cliente e no servidor, ainda tendo que se comportar como se fosse tudo no mesmo local.

O script cliente do aplicativo VoteR define métodos para receber chamadas do servidor, bem como métodos comuns e eventos conectados. Na Figura 3, a primeira linha do código retém uma variável chamada voteHub. Essa é uma linha direta para uma instância da classe VoteHub. O SignalR cria uma instância do hub para cada cliente que se conecta. O cliente inicia a conexão com uma chamada para $.connection.hub.start, que retorna uma promessa. Isso significa que o código contido não será executado até que seja concluído. Nesse caso, é uma chamada para o método Vote no servidor dentro do evento de clicar do botão de votação. Como você pode ver, ele aprova a ID do item para o qual o usuário está votando para o servidor. Em seguida, o servidor faz o trabalho descrito na Figura 1.

Figura 3 Código do cliente JavaScript

$(function () {
  var voteHub = $.connection.voteHub;
  $.connection.hub.start().done(function () {
    $("button").click(function () {
      voteHub.server.vote(this.id);
        });
  });
  voteHub.client.updateVoteResults = function (id, vote) {
    // Update UI to show each item and how many votes it has
    }
  voteHub.client.joinVoting = function (votes) {
    // Cycle through votes to display current information
    // about each item to newcomer
  }   
});

Nesse ponto, você pode pensar que há erros de digitação na Figura 3. Isso porque a nomenclatura da classe VoteHub e do método Vote é inconsistente entre o servidor e o cliente. Isso não é um erro de digitação, mas sim uma convenção SignalR. Nos clientes JavaScript, chamadas para o hub.server.methodName vão em camelCase por padrão. É muito fácil alterar esse comportamento anexando o atributo HubName à classe Hub com a capitalização exata que você deseja. O atributo HubName se parece com o seguinte: HubName(“VoteHub”).

As duas partes mais interessantes do código na Figura 3 são os blocos voteHub.client.updateVoteResults e voteHub.client.joinVoting. Conforme suas assinaturas indicam, ambos os métodos são membros da propriedade Client do VoteHub no servidor. Refletindo sobre a Figura 1, o método voteHub.client.updateVoteResults do lado do cliente na Figura 3 se alinha com a chamada Clients.All.update­VoteResults(id, votes) da Figura 1. A Figura 4 mostra a relação entre o servidor e o código do cliente.

A relação entre o hub e as chamadas de método do cliente
Figura 4 A relação entre o hub e as chamadas de método do cliente

Agora, é momento de examinar o cliente .NET. A Figura 5 mostra algum código que faz uma conexão de um aplicativo Windows Store XAML usando C#. Poderia muito bem ser um aplicativo do Windows Phone, pois o código é idêntico. Começa com a criação de uma conexão para o pipeline SignalR. Em seguida, ele passa a criar o proxy.

O que você vê na Figura 5 que você não vê normalmente no cliente JavaScript é o caminho HTTP para o pipeline SignalR aprovado para o construtor HubConnection. Diferente do cliente JavaScript, ele é um pouco menos automático. Você deve instanciar um HubConnection e chamar CreateHubProxy para criar o proxy.

Figura 5 Código do cliente C# do Windows Store para Vote no VoteR

async private Task startConnection()
{
  var hubConnection = new HubConnection("http://localhost:25024/signalr");
  IHubProxy hubProxy = hubConnection.CreateHubProxy("VoteHub");
  var context = SynchronizationContext.Current;
  hubProxy.On<string, string>("updateVoteResults", (id, votes) =>
    context.Post(delegate {
  // Update UI
); }, null));                        
  await hubConnection.Start();
  await hubProxy.Invoke("vote", "rachel", 
    Convert.ToDecimal(itemId.Text));
}

Observe que na Figura 5 há uma atribuição que obtém um contexto de sincronização. Encapsule o código do cliente que o servidor chama com este objeto de contexto. Em seguida, chame seu método Post e aprove um representante. As delegações no C# são as mesmas que as funções anônimas em linha no JavaScript.

Depois de iniciar a conexão do hub, você pode chamar o método Invoke do proxy para votar em um item. Usar a palavra-chave “await” faz com que ele execute essas ações de forma assíncrona. Você pode encontrar a fonte completa para o aplicativo de demonstração VoteR em: github.com/rachelappel/VoteR.

Implantação do SignalR: Servidor e Cliente

Como o SignalR é ASP.NET, você deve implantar em um ambiente com o NET Framework 4.5 ou posterior, a menos que você se auto-hospede. Em um projeto ASP.NET, o SignalR é apenas um conjunto de bibliotecas. Ele acompanha as outras bibliotecas quando for hora de implantar.

Se você acha que o resto do SignalR é fácil, espere até tentar implantar no Microsoft Azure. Usar o Azure torna o processo de implantação especialmente livre de estresse. Isso também implanta o componente de servidor SignalR e pelo menos um cliente HTML para um servidor Web. Claro, você deve publicar quaisquer aplicativos do Windows Store ou Windows Phone para a App Store e aplicativos da área de trabalho para áreas de trabalho através de seus canais apropriados.

Aqueles com qualquer projeto ASP.NET podem escolher Publicar no menu de compilação do Visual Studio para iniciar a implantação do Azure. Se você estiver usando o Visual Studio 2013, é possível apenas seguir os prompts. Você só precisa inserir suas credenciais e escolher o banco de dados e o nome do site.

No Visual Studio 2012, é um conjunto semelhante de prompts. Durante a implantação, é possível escolher criar um novo site Azure ou selecionar um existente. Se for um site existente, entre no Portal Azure, navegue até a guia Configuração e localize e ative o WebSockets. Você deve fazer isso com um novo site também, mas o Visual Studio criará e lançará o site primeiro, o que causará um erro. Novamente, entre e ative o WebSockets. Isso é uma coisa importante. É uma boa ideia parar e iniciar qualquer site após uma alteração de configuração.

Conclusão

O SignalR é uma programação em tempo real realmente simples. Embora seja um produto ASP.NET, é uma plataforma cruzada em que você pode gravar em aplicativos Windows Store, iOS e Android com um componente de servidor ASP.NET. Você também pode se auto-hospedar em sistemas operacionais que não sejam Microsoft. Isso torna o SignalR flexível, assim como simples e eficiente. Outra grande coisa sobre o SignalR é que você sequer precisa ter a funcionalidade de tempo real como um requisito. Use-o daqui pra frente e junte-se a muitos que já adotaram o paradigma de programação em tempo real.


Rachel Appel é consultora, autora, mentora e antiga funcionária da Microsoft com mais de 20 anos de experiência no setor de TI. Ela dá palestras em importantes congressos do setor, como o Visual Studio Live!, o DevConnections, o MIX e muitos outros. Sua experiência está ligada a soluções de desenvolvimento que alinham negócios e tecnologia com foco na pilha de desenvolvimento da Microsoft e em Web aberta. Para obter mais informações sobre a Appel, visite seu site em rachelappel.com.

Agradecemos ao seguinte especialista técnico da Microsoft pela revisão deste artigo: Frank La Vigne