Server-Sent Events in ASP.NET Core for Real-Time Push Notifications

Push notifications are an important feature in modern web apps, allowing real-time updates without needing to constantly check for changes. One way to do this is with Server-Sent Events (SSE). Unlike WebSockets, which allow two-way communication, SSE is a simpler method for sending data from the server to the client using HTTP. It's perfect for sending updates like notifications, stock prices, or live scores.

In this blog, we'll learn how to set up Server-Sent Events (SSE) in ASP.NET Core to send real-time push notifications to clients.

Why Use Server-Sent Events (SSE)?

  • Easy to Use: SSE works over standard HTTP, making it compatible with firewalls and proxies.
  • One-Way Communication: It's perfect for sending data from the server to clients, like notifications or updates, without needing two-way communication.
  • Automatic Reconnect: If the connection drops, SSE will automatically try to reconnect.
  • Good for Many Clients: SSE is efficient when the server needs to send updates to lots of clients at the same time.

Setting Up SSE in ASP.NET Core

The source code can be downloaded from GitHub.

Let us go over the steps to build a notification service using SSE in ASP.NET Core.

Step 1. Create a New ASP.NET Core Project.

Start by creating a new ASP.NET Core Web API project if you don't have one already.

Step 2. SSE Subscription Endpoint in ASP.NET Core.

In this part, we will set up an SSE endpoint that clients can connect to. The server will send notifications to the clients through this endpoint.

public static class NotificationSubscriptionEndpoints
{
    public static void MapNotificationSubscriptionEndpoints(this WebApplication app)
    {
        app.MapGet("api/notifications/subscribe/{appId}/{userId}", 
            async ([FromRoute] string appId, [FromRoute] string userId, 
            IServerSentEventService sseService, HttpContext context) =>
        {
            if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(userId))
            {
                context.Response.StatusCode = StatusCodes.Status400BadRequest;
                return;
            }

            await sseService.SubscribeToNotificationsAsync(appId, userId, context);
        });
    }
}

Step 3. Sending Notifications to Clients.

To send notifications to subscribed clients, we need a way to send messages to the correct client's SSE stream.

public static class NotificationSendEndpoints
{
    public static void MapNotificationSendEndpoints(this WebApplication app)
    {
        app.MapPost("api/notifications/send/{appId}/{userId}", 
            async ([FromRoute] string appId, [FromRoute] string userId, 
            IServerSentEventService sseService, HttpContext context) =>
        {
            if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(userId))
            {
                context.Response.StatusCode = StatusCodes.Status400BadRequest;
                return;
            }

            var message = "validating the SSE"; //await new StreamReader(context.Request.Body).ReadToEndAsync();
            await sseService.SendNotificationToClientsAsync(appId, userId, message);
        });
    }
}

Step 4. Keeping the SSE Connection Active.

To keep the SSE connection active, the server sends a "ping" message every 10 seconds. This helps prevent timeouts caused by intermediaries like proxies or load balancers.

// Send ping to keep connection alive

await Task.Delay(10000);

await SendMessageToClient(connectionId, "ping");

This sends a "ping" message to the client, ensuring the connection remains open.

Ping

Setting Up the Client-Side: Angular

I have set up an Angular client app to receive notifications from the ASP.NET Core SSE endpoint. I have created a component that subscribes to the notification stream for a specific user and displays incoming messages in real time.

The source code for the client code is available on GitHub. I would request you to clone and look at it.

Angular Client App

Note. If your Angular app is hosted separately from your ASP.NET Core API, you will need to configure CORS to allow requests from the Angular domain.

Managing Connections in a Load-Balanced Environment

In a load-balanced environment, SSE connections may be directed to different servers. To ensure everything works correctly.

  • Sticky Sessions: Make sure your load balancer supports sticky sessions (session affinity) so that a client consistently connects to the same server instance.
  • Centralized Message Store: Another option is to use a centralized message store, such as RabbitMQ or SQL Server. Each server instance can listen for messages from this central store and send them to the connected clients.

Conclusion

Server-sent events (SSE) offer an easy and efficient way to send notifications and updates from a server to a client. In this blog, we built an SSE-based notification system using ASP.NET Core and Angular. SSE is perfect for one-way communication from the server to the client and is great for things like notifications, live feeds, and real-time updates.

By following this guide, you can quickly set up SSE in your own applications and create real-time experiences with little effort.

Happy Coding!