HTML 5 Server-Sent Events: Notificaciones Push

Hasta ahora, la forma más elegante que teníamos para establecer conexiones con el servidor de una forma más o menos “transparente” para el usuario, con el fin de mostrar notificaciones, era a través de peticiones AJAX. Sin embargo, este tipo de técnica no permite que sea el servidor el que inicie la transferencia de datos y debemos estar continuamente preguntando a este si tiene información nueva para nosotros. Aquí es donde entra en juego Server-Sent Events.

Server-Sent Events es una de las especificaciones de HTML 5 en la que se lleva trabajando bastante tiempo (de hecho el primer borrador apareció en el año 2009) y es por ello que la mayoría de los navegadores llevan soportándola desde hace tiempo. El único que se resiste a implementar dicha funcionalidad es Internet Explorer, aunque ante esta situación podemos hacer uso de SignalR, donde el navegador de Microsoft hará uso de Web Sockets y los navegadores que no tuvieran disponible esta funcionalidad utilizarían Server-Sent Events como fallback.

En este post vamos a ver un ejemplo muy simple sobre cómo implementar la parte de cliente y la parte de servidor utilizando ASP.NET MVC.

Servidor

using System;
using System.Text;
using System.Web.Mvc;

namespace ServerSentEvents.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult MyEvent()
        {
            System.Threading.Thread.Sleep(10000);
            var sb = new StringBuilder();
            sb.AppendFormat("data: {0}nn", DateTime.Now);

            return Content(sb.ToString(), "text/event-stream");
        }
    }
}

La parte de servidor es muy sencilla: He creado una acción en mi controlador que básicamente espera unos segundos y devuelve un resultado de tipo Content con un DateTime.Now y el content type “text/event-stream” para que se incluya en la cabecera de la respuesta y poder hacer peticiones de este tipo. El formato “data: “ es necesario para que sea interpretado como el payload del evento.

Cliente

<div class="row">
    <div class="col-md-4">
        <h2>Server-Sent Events</h2>

    </div>
</div>
<script>
    window.onload = function () {
        var source = new EventSource("/Home/MyEvent");

        source.onopen = function () {
            console.log("Connection opened!");
        };

        source.onerror = function (event) {
            if (event.eventPhase == EventSource.CLOSED)
                console.log("Connection closed");
            else
                console.error(event.eventPhase);
        };

        source.onmessage = function (event) {
            console.log("Origin: " + event.origin + " Action:" + event.target.url + " Data: " + event.data);
        };
    };
</script>

En el otro extremo, debemos hacer uso del objeto EventSource, el cual recibe como parámetro la URL de la cual espera actualizaciones. Para ser conscientes de lo que ocurre con dicho objeto, debemos manejar los siguientes eventos:

  • open: La conexión ha sido establecida. Este evento ocurre cada vez que el servidor envía un mensaje.
  • error: Como es obvio, se utiliza para controlar los errores producidos. Sin embargo, cuando la conexión es cerrada una vez enviado el mensaje, he detectado que lanza un error, por lo que compruebo si la propiedad eventPhase es igual a EventSource.CLOSED (2) para mostrar un mensaje más amigable. Por lo que he podido comprobar este comportamiento se repite con otros lenguajes (probé en PHP también por si era algún problema con la implementación en .NET y el resultado es exactamente el mismo)
  • message: Cada vez que el servidor envía un mensaje lo capturamos a través de este evento, donde además podemos conseguir algo más de información como el origin, la URL exacta desde donde nos llega la información y por supuesto los datos que hemos recibido desde el servidor.

Este es el resultado que podemos ver por consola:

Server-Sent Events

Server-Sent Events vs WebSockets

Puede ser que te estés preguntando qué diferencia existe entre Server-Sent Events y WebSockets ya que ambas parecen hacer lo mismo: ser capaces de que sea el servidor el que comience la conversación con el cliente. Sin embargo, WebSockets permite una comunicación bidireccional y full duplex entre el cliente y el servidor mientras que Server-Sent Events sólo permite mensajes desde el servidor al cliente.

Espero que haya sido de utilidad 🙂

CanIUse.com Server-Sent Events

¡Saludos!

  • Pingback: Bitacoras.com()

  • Pingback: @Jtorrecilla()

  • Pingback: @HTML5MeetSpain()

  • gerardo

    que buen aporte me imagino que podria usar json
    con php

    saludos

    • Gisela

      Hola gerardo,

      Si, es posible usar JSON, ya que simplemente es un texto con un formato concreto. He modificado la demo para que puedas verlo:

      Del lado del servidor, modifico el mensaje a enviar por el siguiente:

      sb.AppendFormat(“data: {{“greeting”:”Hello World at {0}!”}}nn”, DateTime.Now);

      De esta forma la información llegará en un formato correcto para obtener un Object Literal de él. En el lado del cliente, he modificado el handler para el evento onmessage, donde hago uso de JSON.parse(event.data).greeting para obtener la propiedad de mi nuevo objecto 🙂

      source.onmessage = function (event) {
      console.log(“Origin: ” + event.origin + ” Action:” + event.target.url + ” Data: ” + JSON.parse(event.data).greeting);
      };

      Espero que se haya resuelto tu duda 🙂

      ¡Saludos!

  • Pingback: gerardo pacheco (@chalchis60)()

  • Pingback: Gisela Torres (@0GiS0)()

  • gerardo

    orale que bien muy amable Gisela exelente!!!

    muchas gracias saludos!!! 😉

  • gerardo

    hola Gisela quiero implementar esto de server sent pero encontre una incosistencia con firefox en ie mencionan que no es posible
    la inconsistencia que encontre es que de repende se muere la conexion y ya el server deja de mandar los mensajes desde firefox en safari y chrome va bien
    entonces encontr esto no se que me puedas opinar al respecto en la forma de aplicarlo

    https://github.com/Yaffle/EventSource

    saludos