ASP.NET Core cookbook · Intermediate
Stream updates to browsers with Server-Sent Events
The UI polls every second for order status updates.
Approach
Return TypedResults.ServerSentEvents (.NET 10) with an IAsyncEnumerable that yields events as they happen,
honouring the request's cancellation token so the stream ends when the client disconnects.
Solution
using System.Net.ServerSentEvents;
using System.Runtime.CompilerServices;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/api/orders/{id}/events", (string id, CancellationToken ct) =>
TypedResults.ServerSentEvents(OrderStatusUpdates(id, ct)));
app.Run();
static async IAsyncEnumerable<SseItem<OrderStatus>> OrderStatusUpdates(string id, [EnumeratorCancellation] CancellationToken ct)
{
string[] statuses = ["paid", "packed", "shipped"];
foreach (var status in statuses)
{
await Task.Delay(TimeSpan.FromSeconds(2), ct); // in a real app: read from a channel or message bus
yield return new SseItem<OrderStatus>(new OrderStatus(id, status), eventType: "status");
}
}
public record OrderStatus(string OrderId, string Status);
// Browser
const source = new EventSource("/api/orders/o42/events");
source.addEventListener("status", (event) => {
const update = JSON.parse((event as MessageEvent).data);
console.log(`order ${update.orderId}: ${update.status}`);
});
Pitfalls
- Proxies or response compression buffering the stream; disable buffering for the route.
- No cancellation, leaving server loops running after clients leave.
- Using SSE for bidirectional messaging; use WebSockets or SignalR for that.
Variations
- Back the stream with
Channel<T>or Redis pub/sub so events from other instances reach the client. - SignalR adds fallbacks, groups and scale-out for richer real-time features.
Further reading: Server-Sent Events in minimal APIs
More ASP.NET Core recipes
- Accept file uploads with size and type limits
- Add OpenTelemetry traces and metrics
- Apply EF Core migrations safely during deployment
- Bind and validate options at startup
- Cache responses with output caching and evict by tag
- Call external APIs with typed HttpClients and resilience
- Configure CORS for a browser front end
- Expose liveness and readiness health checks