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 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!