loading experience

Sviluppo

Real-Time Streaming in .NET 10: Dì Addio al Polling con le Server-Sent Events (SSE)

Nello sviluppo web moderno, la necessità di aggiornare l'interfaccia utente in tempo reale è ormai uno standard. Storicamente, l'approccio più semplice (ma meno efficiente) è stato il HTTP Polling, in cui il client tempesta il server di richieste a intervalli regolari.

Real-Time Streaming in .NET 10: Dì Addio al Polling con le Server-Sent Events (SSE)


Nello sviluppo web moderno, la necessità di aggiornare l'interfaccia utente in tempo reale è ormai uno standard. Storicamente, l'approccio più semplice (ma meno efficiente) è stato il HTTP Polling, in cui il client tempesta il server di richieste a intervalli regolari.

Questo approccio comporta un enorme spreco di risorse, latenza e un carico inutile sul server.

Mentre per comunicazioni bidirezionali complesse la scelta ideale ricade su SignalR (WebSockets), esiste uno spazio intermedio perfetto per flussi di dati unidirezionali (dal server al client): le Server-Sent Events (SSE). Con il rilascio di .NET 10, ASP.NET Core introduce finalmente un supporto nativo di prima classe che rende l'implementazione di SSE incredibilmente semplice e pulita.


Perché scegliere SSE invece del Polling (e di SignalR)?

Le Server-Sent Events si basano su una singola connessione HTTP standard che rimane aperta. Il server spinge i dati verso il client non appena sono disponibili, formattati secondo il tipo text/event-stream.

  1. Nessun Polling: Il client effettua una sola richiesta HTTP. Nessun ciclo infinito di richieste ogni $X$ secondi.
  2. Infrastruttura Standard: Funziona su HTTP/1.1 o HTTP/2, senza bisogno di aggiornamenti di protocollo (come richiesto dai WebSockets). Funziona nativamente con proxy, firewall e bilanciatori di carico.
  3. Riconnessione Automatica: Se la rete cade, il browser tenta automaticamente di riconnettersi tramite l'API nativa EventSource, inviando l'ultimo ID ricevuto (Last-Event-ID) per evitare la perdita di dati.
  4. Leggerezza: Rispetto a SignalR, non richiede librerie client pesanti o configurazioni complesse sul server.


La Svolta di .NET 10: Results.ServerSentEvents

Nelle versioni precedenti di .NET, per implementare SSE era necessario configurare manualmente gli header di HttpContext.Response, scrivere stringhe formattate nel body dello stream e gestire manualmente il flush del buffer e i token di cancellazione.

In .NET 10, tutto questo viene estratto grazie al nuovo metodo Results.ServerSentEvents (o TypedResults.ServerSentEvents) e alla struct nativa SseItem<T> inclusa nel namespace System.Net.ServerSentEvents. Il framework si occupa autonomamente di serializzare in JSON, gestire la formattazione dello stream e rispettare la cancellazione del client.


Esempio Pratico: Streaming di Aggiornamenti con .NET 10

Di seguito viene mostrato un esempio completo. Il backend simula un flusso continuo di dati meteo inviati al client ogni 2 secondi senza interruzioni.

1. Il Backend (.NET 10 Minimal API)

Assicurati di utilizzare l'SDK di .NET 10. Crea un endpoint che restituisce un IAsyncEnumerable integrato con SseItem<T>.

C#


using System.Net.ServerSentEvents;
using System.Runtime.CompilerServices;

var builder = WebApplication.CreateBuilder(args);

// Abilitiamo CORS per permettere al frontend di connettersi liberamente
builder.Services.AddCors(options => options.AddDefaultPolicy(policy =>
policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()));

var app = builder.Build();
app.UseCors();

// Endpoint SSE che invia flussi di dati in tempo reale senza polling
app.MapGet("/api/weather-stream", async ([EnumeratorCancellation] CancellationToken ct) =>
{
// Funzione locale per generare uno stream asincrono di dati
async IAsyncEnumerable<SseItem<WeatherPayload>> GenerateWeatherUpdates([EnumeratorCancellation] CancellationToken cancellationToken)
{
var random = new Random();
int eventId = 1;

while (!cancellationToken.IsCancellationRequested)
{
var payload = new WeatherPayload(
DateTime.Now.ToString("HH:mm:ss"),
random.Next(-5, 38)
);

// SseItem incapsula il dato e i metadati dell'evento (es. l'ID per la riconnessione)
yield return new SseItem<WeatherPayload>(payload)
{
EventId = eventId.ToString()
};

eventId++;
// Attendiamo 2 secondi prima del prossimo invio
await Task.Delay(2000, cancellationToken);
}
}

// .NET 10 gestisce automaticamente l'header text/event-stream e il flushing dei dati
return TypedResults.ServerSentEvents(GenerateWeatherUpdates(ct));
});

app.Run();

// Record per il payload dei dati
public record WeatherPayload(string Time, int TemperatureC);


2. Il Frontend (Vanilla JavaScript)

Sul lato client non serve alcuna libreria esterna. Sfruttiamo l'oggetto nativo EventSource messo a disposizione da tutti i browser moderni.

HTML


<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>SSE Real-Time Stream (.NET 10)</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; background-color: #f4f4f9; }
#log { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); max-width: 500px; }
.event-item { padding: 8px; border-bottom: 1px solid #eee; }
</style>
</head>
<body>

<h2>Aggiornamenti Meteo in Tempo Reale (No Polling)</h2>
<div id="log">In attesa di dati dal server...</div>

<script>
// Inizializza la connessione persistente verso l'endpoint .NET 10
const eventSource = new EventSource('http://localhost:5000/api/weather-stream');
const logDiv = document.getElementById('log');

// Questo evento si attiva ogni volta che il server fa uno "yield return" di un SseItem
eventSource.onmessage = function(event) {
// I dati arrivano come stringa JSON
const data = JSON.parse(event.data);
if(logDiv.innerText.includes("In attesa")) {
logDiv.innerHTML = '';
}

const item = document.createElement('div');
item.className = 'event-item';
item.innerHTML = `<strong>Ore:</strong> ${data.time} | <strong>Temperatura:</strong> ${data.temperatureC}°C (ID Evento: ${event.lastEventId})`;
logDiv.insertBefore(item, logDiv.firstChild);
};

// Gestione degli errori o delle disconnessioni
eventSource.onerror = function(error) {
console.error("Connessione interrotta o errore di rete. Il browser proverà a riconnettersi automaticamente...", error);
};
</script>
</body>
</html>


Conclusioni

Con .NET 10, le Server-Sent Events escono dall'ombra delle implementazioni custom per diventare un cittadino di prima classe in ASP.NET Core.

Se la tua applicazione ha bisogno esclusivamente di ricevere dati aggiornati dal server (come nel caso di dashboard, feed di notifiche, log in streaming o risposte progressive da modelli di Intelligenza Artificiale/LLM), SSE con TypedResults.ServerSentEvents è la soluzione architetturalmente più pulita, leggera ed efficiente a tua disposizione, mandando definitivamente in pensione il vecchio e pesante meccanismo del polling.

Commenti (0)

Nessun commento ancora.

Lascia un commento