Atualizado: 07/12/2024

Server-Sent Events - JavaScript

Server-Sent Events, ou abreviado SSE, representa uma tecnologia de interação entre cliente e servidor que permite ao servidor enviar mensagens ao cliente. Vale destacar que, diferente dos WebSockets, a comunicação através de Server-Sent Events é unidirecional: as mensagens são entregues em uma única direção, do servidor para o cliente (como o navegador web do usuário). Isso os torna uma ótima escolha quando não é necessário enviar dados do cliente para o servidor. Por exemplo, Server-Sent Events podem ser utilizados para tratar coisas como atualização de status em redes sociais, feeds de notícias ou envio de dados para armazenamento no lado do cliente.

Na página web, no código JavaScript, para interagir com o servidor, utiliza-se a interface EventSource. O objeto EventSource basicamente representa um servidor que gera eventos ou envia mensagens. Para criar um objeto EventSource, aplica-se o construtor:

new EventSource(url, options)

Como primeiro parâmetro obrigatório no construtor EventSource, passa-se o endereço URL do recurso no servidor:

const evtSource = new EventSource("/events");

Opcionalmente, também é possível passar um parâmetro adicional que configura o objeto EventSource. Este parâmetro representa um objeto com uma propriedade chamada withCredentials. Esta propriedade indica se os cabeçalhos CORS devem ser incluídos para interação entre domínios. Por padrão, ela é false.

Eventos do EventSource

Para gerenciar o estado da conexão no EventSource, existe uma série de eventos definidos:

  • open: disparado ao estabelecer a conexão. Para configurar o manipulador do evento, pode-se usar a propriedade onopen.

  • error: disparado quando ocorre um erro ao estabelecer a conexão. Para configurar o manipulador do evento, pode-se usar a propriedade onerror.

  • message: disparado ao receber dados do servidor. Para configurar o manipulador do evento, pode-se usar a propriedade onmessage.

Os manipuladores desses eventos recebem como parâmetro um objeto padrão do tipo Event. Exemplo de configuração de manipuladores de eventos:

const evtSource = new EventSource("/events");

// usando addEventListener
evtSource.addEventListener("open", () => {
  console.log("conexão estabelecida");
});
evtSource.addEventListener("error", () => {
  console.log("Erro");
});

// usando propriedades
evtSource.onopen = () => {
  console.log("conexão estabelecida");
};
evtSource.onerror = () => {
  console.log("Erro");
};

Recebendo Dados

Quando os dados chegam do servidor, o objeto EventSource dispara o evento message. Para configurar o manipulador deste evento, pode-se usar a propriedade onmessage ou o método addEventListener().

No manipulador do evento message, é passado um objeto do tipo MessageEvent. Este objeto fornece várias propriedades que permitem extrair os dados da resposta do servidor:

  • data: retorna os dados recebidos.

  • origin: armazena o endereço do remetente.

  • lastEventId: armazena o identificador único do último evento como uma string.

  • source: retorna o objeto MessageEventSource, que pode ser um objeto WindowProxy, MessagePort ou ServiceWorker, representando o remetente dos dados recebidos.

  • ports: retorna um array de objetos MessagePort, que armazenam as portas usadas para o envio.

Exemplo de recebimento de dados:

const evtSource = new EventSource("/events");
evtSource.onmessage = (event) => { 
    console.log(event.data); // exibe os dados recebidos no console
};

Fechando a Conexão

Para fechar a conexão, usa-se o método close():

evtSource.close();

Exemplo de Interação entre Cliente e Servidor com Server-Sent Events

Vamos considerar um pequeno exemplo de interação entre cliente e servidor utilizando Server-Sent Events. O cliente será representado por um código JavaScript em uma página web e o servidor será implementado com Node.js.

Primeiro, vamos definir o código do servidor. Para isso, criaremos um arquivo server.js com o seguinte código:

const http = require("http");
const fs = require("fs");

// dados para envio ao cliente
const messages = ["Olá", "Como vai?", "O que está fazendo?", "Você está dormindo?", "Adeus "];
http.createServer(function(request, response) {
    if(request.url == "/events") {   // se a requisição for SSE
        if (request.headers.accept && request.headers.accept === "text/event-stream") {
            sendEvent(response);
        }
        else {
            response.writeHead(400);
            response.end("Bad Request");
        }
    }
    else {   // em outros casos, envia a página index.html
        fs.readFile("index.html", (_, data) => response.end(data));
    }
}).listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));

// função para enviar mensagem ao cliente
function sendEvent(response) { 
    // configurar cabeçalhos
    response.writeHead(200, {    
        "Content-Type": "text/event-stream",    
        "Cache-Control": "no-cache",    
        "Connection": "keep-alive" 
    });
    const id = (new Date()).toLocaleTimeString();  // definir o identificador do último evento
    // a cada 5 segundos enviar uma mensagem
    setInterval(() => { createServerSendEvent(response, id); }, 5000);
}

// função para criar e enviar eventos ao cliente
function createServerSendEvent(response, id) {
    // gerar número aleatório - índice para o array messages
    const index = Math.floor(Math.random() * messages.length);
    const message = messages[index];
    response.write("id: " + id + "\n");  
    response.write("data: " + message + "\n\n");
}

Explicação do Código do Servidor

Primeiro, importamos os módulos necessários:

const http = require("http");   // para lidar com requisições HTTP
const fs = require("fs");       // para leitura de arquivos do disco

Em seguida, definimos um conjunto de mensagens que serão enviadas ao cliente:

const messages = ["Olá", "Como vai?", "O que está fazendo?", "Você está dormindo?", "Adeus "];

Para criar o servidor, usamos a função http.createServer(), que recebe uma função de callback chamada sempre que uma requisição é feita ao servidor. Esta função tem dois parâmetros: request (contendo os dados da requisição) e response (utilizado para enviar a resposta).

Na função de callback, verificamos a URL da requisição usando request.url. Se a URL for "/events", tratamos como uma requisição SSE:

if(request.url == "/events") {   // se a requisição for SSE
  if (request.headers.accept && request.headers.accept === "text/event-stream") {
      sendEvent(response);
  }
  else {
      response.writeHead(400);
      response.end("Bad Request");
  }
}

Para que a requisição seja tratada como SSE, o cabeçalho Accept deve ter o valor text/event-stream. Se esse cabeçalho estiver presente, chamamos a função sendEvent(), passando o objeto response. Caso contrário, enviamos um erro 400 (Bad Request).

Na função sendEvent(), configuramos os cabeçalhos da resposta e iniciamos um intervalo que envia dados ao cliente a cada 5 segundos:

function sendEvent(response) { 
  response.writeHead(200, {    
      "Content-Type": "text/event-stream",    
      "Cache-Control": "no-cache",    
      "Connection": "keep-alive" 
  });
  const id = (new Date()).toLocaleTimeString();  // definir o identificador do último evento
  setInterval(() => { createServerSendEvent(response, id); }, 5000);
}

A função createServerSendEvent() gera um índice aleatório para selecionar uma mensagem do array messages e envia essa mensagem ao cliente:

function createServerSendEvent(response, id) {
  const index = Math.floor(Math.random() * messages.length);
  const message = messages[index];
  response.write("id: " + id + "\n");  
  response.write("data: " + message + "\n\n");
}

Se a URL da requisição não for "/events", enviamos o arquivo index.html:

else {
  fs.readFile("index.html", (_, data) => response.end(data));
}

Finalmente, iniciamos o servidor na porta 3000:

.listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));

Código do Cliente

Agora, vamos definir um arquivo simples index.html na mesma pasta do servidor:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Programício</title>
</head>
<body>
<ul id="list"></ul>
<script>
const source = new EventSource("/events");
const list = document.getElementById("list");

source.addEventListener("message", (e) => {
    const listItem = document.createElement("li");
    listItem.textContent = e.data;
    list.appendChild(listItem);
});
</script>
</body>
</html>

Neste arquivo, ao receber dados do servidor, adicionamos esses dados a uma lista na página web.

Executando o Servidor

Na linha de comando, navegue até a pasta do servidor e inicie-o com o comando:

node server.js
O servidor será iniciado em http://localhost:3000.

Após iniciar o servidor, abra um navegador e acesse http://localhost:3000. A página exibirá os dados recebidos do servidor em intervalos regulares de 5 segundos.

Exemplo de interação entre cliente e servidor com Server-Sent Events
Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com