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
EventSource
EventSource
new EventSource(url, options)
Como primeiro parâmetro obrigatório no construtor EventSource
const evtSource = new EventSource("/events");
Opcionalmente, também é possível passar um parâmetro adicional que configura o objeto EventSource
withCredentials
false
Eventos do EventSource
Para gerenciar o estado da conexão no EventSource
: disparado ao estabelecer a conexão. Para configurar o manipulador do evento, pode-se usar a propriedadeopen
.onopen
: disparado quando ocorre um erro ao estabelecer a conexão. Para configurar o manipulador do evento, pode-se usar a propriedadeerror
.onerror
: disparado ao receber dados do servidor. Para configurar o manipulador do evento, pode-se usar a propriedademessage
.onmessage
Os manipuladores desses eventos recebem como parâmetro um objeto padrão do tipo Event
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
message
onmessage
addEventListener()
No manipulador do evento message
MessageEvent
: retorna os dados recebidos.data
: armazena o endereço do remetente.origin
: armazena o identificador único do último evento como uma string.lastEventId
: retorna o objetosource
, que pode ser um objetoMessageEventSource
,WindowProxy
ouMessagePort
, representando o remetente dos dados recebidos.ServiceWorker
: retorna um array de objetosports
, que armazenam as portas usadas para o envio.MessagePort
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
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()
request
response
Na função de callback, verificamos a URL da requisição usando request.url
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
text/event-stream
sendEvent()
response
Na função sendEvent()
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()
messages
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
<!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
http://localhost:3000
Após iniciar o servidor, abra um navegador e acesse http://localhost:3000
