Recebendo Notificações com SSE


É muito comum aplicações Web precisarem receber informações do servidor para que seja possível atualizar a página em que o usuário se encontra, ou até mesmo para receber notificações de que algo ocorreu e que isso possa interessar ao mesmo. Existem diversas técnicas que possibilitam esse objetivo, e uma das mais conhecidas é o polling, que consiste em criar um timer no cliente (HTML/JavaScript) que periodicamente verifica no servidor se há alguma atualização.

O problema desta técnica é ter que ficar gastando tempo e recurso, e dependendo da forma como as informações são disponibilizadas, podemos demandar todo este recurso para que não se receber nada. Um novo recurso, disponível a partir do HTML 5, é o Web Sockets, possui uma vasta gama de recursos, incluindo a comunicação bidirectional entre o cliente e o servidor, e pode atender completamente esta demanda. Só que em algumas vezes precisamos apenas do recebimento de informações do servidor, e o Web Sockets para soar como um excesso para essa atividade.

O HTML 5 ainda fornece um recurso chamado de Server Sent Events (SSE), e com uma API de fácil utilização, é possível estabelecer a comunicação e receber as notificações do servidor. O ASP.NET Web API também fornece uma forma de estabelecer, via stream, uma ligação com o cliente, e qualquer coisa que se escreva neste stream, o mesmo receberá e irá tratar a mesma como quiser. A finalidade deste artigo é explorar a utilização do SSE para notificar os usuários sobre novas notícias que acontecerão.

Vamos iniciar pelo lado do servidor. Iremos criar um controller no ASP.NET Web API para gerenciar os assinantes, receber as notícias e publicá-las. Podemos reparar na classe abaixo que há uma coleção chamada assinantes que armazenará os streams dos clientes conectados. O método Assinar é responsável por receber a requisição dos clientes interessados, e no construtor da classe PushStreamContent definimos um delegate que irá instanciar o StreamWriter e armazenará na coleção.

public class NoticiasController : ApiController
{
    private static ConcurrentBag<StreamWriter> assinantes = new ConcurrentBag<StreamWriter>();

    [HttpGet]
    public HttpResponseMessage Assinar(HttpRequestMessage requisicao)
    {
        return new HttpResponseMessage()
        {
            Content =
                new PushStreamContent(
                    (stream, content, context) =>
                        assinantes.Add(new StreamWriter(stream) { AutoFlush = true }),
                    “text/event-stream”)
        };
    }
}

Nós já utilizamos a classe PushStreamContent no artigo que vimos como exibir vídeos pelo ASP.NET Web API, e da mesma forma aqui, o StreamWriter que ela armazenará servirá para que as strings sendo escritas nele viajem até os respectivos clientes. Por fim, o media-type text/event-stream que quando informado no Content-Type da resposta, permite aos clientes entender que esta resposta seja entendida como um canal que receberá constantes informações.

Uma vez que temos os assinantes, chega o momento da geração do conteúdo e, principalmente, a entrega das informações para os clientes. O primeiro método que vemos abaixo, AdicionarNoticia, recebe uma nova notícia, armazena em algum repositório e notificamos os clientes que estão adicionados na coleção.

[HttpPost]
public async Task AdicionarNoticia(Noticia noticia)
{
    //Armazenar em algum repositório

    await Notificar(noticia);
}

private async static Task Notificar(Noticia noticia)
{
    foreach (var assinante in assinantes.ToList())
    {
        try
        {
            await assinante.WriteAsync(
                string.Format(“data: {0}nn”, JsonConvert.SerializeObject(noticia)));
        }
        catch
        {
            StreamWriter sw = null;
            assinantes.TryTake(out sw);
        }
    }
}

O método Notificar percorre a coleção e via o método WriteAsync do Stream publicamos a nova notícia aos clientes. Como sabemos, o StreamWriter nos permite mandar strings, e através de um serializador (neste caso em formato JSON) temos a oportunidade de mandarmos objetos complexos, e como o JavaScript lida de forma fácil com este formato, não teremos muitos problemas em exibir essa informação. A única preocupação que temos que ter é com relação a como mandamos a string para o cliente, ou seja, prefixando com o “data: MensagemAquinn”.

Agora que já temos tudo o que é necessário do lado do servidor, chega o momento em que temos que codificar o cliente, e assim utilizando efetivamente o SSE. Infelizmente o Internet Explorer não dá suporte à este recurso, e para o exemplo, vamos utilizar o Chrome. A primeira condicional é para avaliar se o navegador dá suporte para o SSE. Caso seja suportado, então instanciamos a classe EventSource e informamos o endereço para o método Assinar. Uma vez que a ligação estiver estabelecidade, as mensagens começarão a chegar, e vamos capturar essas mensagens através do evento onMessage, fazemos o parse da string transformando-a em JSON e, consequentemente, acessar as propriedades.

    if (!!window.EventSource) {
        var sse = new EventSource(“/api/Noticias/Assinar”);

        sse.onmessage = function (e) {
            $(‘#NovaNoticia’).text($.parseJSON(e.data).Titulo);
            $(‘#AlertaDeNoticia’).modal();
        };
    }

Finalmente, quando o navegador estiver aberto em uma página HTML com o código acima, e uma nova notícia for postada, uma pop-up é aberta exibindo a mesma, assim como é mostrado na imagem acima.

Anúncios

4 comentários sobre “Recebendo Notificações com SSE

  1. Boas Marcel Augusto,

    Do lado do servidor, você pode (e deve) utilizar o Web API para gerenciar os assinantes. Já do lado do cliente, pode ser qualquer aplicação Web, incluindo o ASP.NET MVC. No meu caso, eu criei apenas um arquivo HTML para o teste.

    Nada impede que você crie uma aplicação ASP.NET com suporte para MVC e Web API, e utilize um para o serviço e o outro para o cliente.

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s