Skip to content

Instantly share code, notes, and snippets.

@kallebysantos
Last active May 22, 2023 18:37
Show Gist options
  • Save kallebysantos/5ff16b2abdd7487d377fb8dd1373f103 to your computer and use it in GitHub Desktop.
Save kallebysantos/5ff16b2abdd7487d377fb8dd1373f103 to your computer and use it in GitHub Desktop.
Server Send Events with .NET

.NET Sample implementation of SSE

image

Getting started

Creating our data object

// Message.cs

public record Message(Guid id, string data)
{
    public override string ToString() => $"id: {id}\ndata: {data}\n";
}

The Message record will handle the id and data that will be send via event stream. It also overrides the default ToString() method in order to print Message's data in a streamable format.

Adding a channel service

Since the server will be handling the HTTP requests in parallel. We need a thread safe channel to communicate between theses request handlers.

A System.Threading.Channels.Channel class give us a Writter and Reader allowing to implement a Producer/Consumer pattern.

// Program.cs

var channel = Channel.CreateUnbounded<Message>();

builder.Services.AddSingleton(channel);

Handling new messages

Lets implement a endpoint to handling income messages and write them in our Channel.

// Program.cs

app.MapPost("/send", async ([FromServices] Channel<Message> _channel, [FromQuery] string message) =>
{
    await _channel.Writer.WriteAsync(new Message(id: Guid.NewGuid(), data: message));

    return Results.Ok();
});

Sending Events from Server

// Program.cs

app.MapGet("/event", async ([FromServices] Channel<Message> _channel, HttpContext context) =>
{
    Console.WriteLine("Client listening");
    context.Response.Headers.CacheControl = "no-store";
    context.Response.Headers.ContentType = "text/event-stream";

    while (await _channel.Reader.WaitToReadAsync())
    {
        while (_channel.Reader.TryRead(out Message? message))
        {
            await context.Response.WriteAsync(message.ToString() + '\n');
            await context.Response.Body.FlushAsync();
        }
    }
});

The code above will open an event-stream connection type and than will wait for the Reader to get new messages. When a new Message cames the server will write it to the response stream.

Adding Client Side

// Program.cs

app.UseDefaultFiles();
app.UseStaticFiles();
// wwwroot/index.html

<html>
    <head>
        <title>SEE Test</title>
    </head>

    <body>
        <h1>Listenig for events:</h1>

        <ul id="list">
        </ul>
        <script>
            const evtSource = new EventSource("/event");
            console.log(evtSource); 
            evtSource.onmessage = (event) => {
                console.log("New event:", event)
                const newElement = document.createElement("li");
                const eventList = document.getElementById("list");

                newElement.textContent = `message: ${event.data}`;
                eventList.appendChild(newElement);
            };
        </script>
    </body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment