// 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.
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);
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();
});
// 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.
// 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>