Lib.AspNetCore.ServerSentEvents
repository·main·Indexed 43 days ago
https://github.com/tpeczek/lib.aspnetcore.serversenteventsA library providing specialized support for implementing Server-Sent Events (SSE) within ASP.NET Core applications. It includes features for mapping SSE endpoints, broadcasting messages to all or specific clients, handling auto-reconnects via LastEventId, and managing client disconnections using IServerSentEventsClientIdProvider and IServerSentEventsNoReconnectClientsIdsStore. It also supports ASP.NET Core policy-based authorization and custom HTTP response header configuration via ServerSentEventsOptions.
What's inside Lib.AspNetCore.ServerSentEvents
- Lib.AspNetCore.ServerSentEvents is a library designed to provide Server-Sent Events (SSE) support specifically for ASP.NET Core applications.
Use custom authorization requirements for SSE
mainThe library supports the full power of ASP.NET Core policy-based authorization. To implement custom logic, implementIAuthorizationRequirementand anAuthorizationHandler<TRequirement>(orIAuthorizationHandler), register the resulting policy, and reference it via thePolicyproperty inServerSentEventsOptions.Authorization.What are Groups in Lib.AspNetCore.ServerSentEvents
mainA Group is a named collection of clients managed by the library. Groups provide a recommended way to broadcast messages to multiple clients simultaneously. A single client can be a member of multiple groups at once.Handle SSE auto-reconnect and missed messages
mainSSE supports automatic reconnection and tracking the last seen message ID. When a client reconnects, they can provide the ID of the last message they received so the server can retransmit missed data.
Approach 1: Using the
ClientConnectedeventSubscribe to
IServerSentEventsService.ClientConnected. The event arguments (ServerSentEventsClientConnectedArgs) provide:LastEventId: The identifier of the last message the client received.Client: TheIServerSentEventsClientinstance to which you can send the missed messages.
Approach 2: Overriding
OnReconnectAsyncWhen implementing a custom
ServerSentEventsService, you can overrideOnReconnectAsync(IServerSentEventsClient client, string lastEventId). Note: You must callbase.OnReconnectAsync(client, lastEventId)within your override to ensure theClientConnectedevent is still triggered.Implement and register a "No Reconnect" Identifiers Store
mainThe disconnection flow works by responding with a
204 No Contentstatus code to a reconnection attempt. To make this work, you must implementIServerSentEventsNoReconnectClientsIdsStoreto track which identifiers are forbidden from reconnecting.There are two built-in implementations available:
- In-Memory Store: Use
AddInMemoryServerSentEventsNoReconnectClientsIdsStore()for local, single-instance scenarios. - Distributed Cache Store: Use the distributed cache-backed implementation (method name not explicitly shown in snippet, but available via documentation) for multi-instance/scaled environments.
Both require
IServerSentEventsClientIdProviderto be registered first.public void ConfigureServices(IServiceCollection services) { services.AddServerSentEvents(); // 1. Register the identifier provider services.AddServerSentEventsClientIdProvider<CookieBasedServerSentEventsClientIdProvider>(); // 2. Register the store (In-Memory example) services.AddInMemoryServerSentEventsNoReconnectClientsIdsStore(); }- In-Memory Store: Use
Install and configure Lib.AspNetCore.ServerSentEvents
mainTo add Server-Sent Events (SSE) support to an ASP.NET Core application, you must register the required services and map the middleware.
Basic Setup
Use
AddServerSentEvents()inConfigureServicesto register the default service andMapServerSentEvents(path)inConfigureto map it to an endpoint.Creating Distinguishable Endpoints
By default, all endpoints mapped via
MapServerSentEventsshare the same service instance, making them indistinguishable. To create unique endpoints with unique services (e.g., for different notification types), you must register a custom implementation ofServerSentEventsServiceand map it using the generic overload.public class Startup { public void ConfigureServices(IServiceCollection services) { // Register default service services.AddServerSentEvents(); // Register a custom service for a specific endpoint services.AddServerSentEvents<INotificationsServerSentEventsService, NotificationsServerSentEventsService>(); } public void Configure(IApplicationBuilder app) { // Map the default endpoint app.MapServerSentEvents("/default-sse-endpoint"); // Map a specific endpoint to the custom service app.MapServerSentEvents<NotificationsServerSentEventsService>("/notifications-sse-endpoint"); } } // Custom service implementation example internal interface INotificationsServerSentEventsService : IServerSentEventsService { } internal class NotificationsServerSentEventsService : ServerSentEventsService, INotificationsServerSentEventsService { public NotificationsServerSentEventsService(IOptions<ServerSentEventsServiceOptions<NotificationsServerSentEventsService>> options) : base(options.ToBaseServerSentEventsServiceOptions()) { } }Install Lib.AspNetCore.ServerSentEvents via NuGet
mainTo use Server-Sent Events (SSE) support in your ASP.NET Core application, install the
Lib.AspNetCore.ServerSentEventspackage from NuGet.PM> Install-Package Lib.AspNetCore.ServerSentEventsConfigure Nginx proxy for Server-Sent Events
mainWhen running behind an Nginx proxy, you must ensure that Nginx does not buffer the SSE stream, otherwise, events will not be delivered to the client in real-time. To achieve this, use the
OnPrepareAcceptcallback inServerSentEventsOptionsto append theX-Accel-Buffering: noheader to the response.app.MapServerSentEvents("/default-sse-endpoint", new ServerSentEventsOptions { OnPrepareAccept = response => { response.Headers.Append("Cache-Control", "no-cache"); response.Headers.Append("X-Accel-Buffering", "no"); } });Configure HTTP response headers via ServerSentEventsOptions.OnPrepareAccept
mainYou can customize the HTTP response headers sent during the SSE connection handshake by using the
OnPrepareAcceptdelegate within theServerSentEventsOptionsobject. This is useful for setting specific headers required by clients or intermediate proxies.app.MapServerSentEvents("/default-sse-endpoint", new ServerSentEventsOptions { OnPrepareAccept = response => { response.Headers.Append("Cache-Control", "no-cache"); response.Headers.Append("X-Accel-Buffering", "no"); } });Apply simple authorization to SSE endpoints
mainYou can control access to a Server-Sent Events endpoint using the
Authorizationproperty withinServerSentEventsOptions. SettingAuthorizationtoServerSentEventsAuthorization.Defaultrestricts access to any authenticated user.app.MapServerSentEvents("/default-sse-endpoint", new ServerSentEventsOptions { Authorization = ServerSentEventsAuthorization.Default });Implement and register a Client Identifier Provider
mainTo enable client disconnection, you must provide a way to identify clients consistently across reconnect attempts. You do this by implementing
IServerSentEventsClientIdProvider.This provider must implement two methods:
AcquireClientId(HttpContext context): Returns a unique identifier (e.g., aGuid) for the client. This ID must persist if the client reconnects.ReleaseClientId(Guid clientId, HttpContext context): Cleans up the identifier when appropriate.
Use the
AddServerSentEventsClientIdProvider<T>extension method to register your implementation inIServiceCollection.internal class CookieBasedServerSentEventsClientIdProvider : IServerSentEventsClientIdProvider { private const string COOKIE_NAME = ".ServerSentEvents.Guid"; public Guid AcquireClientId(HttpContext context) { Guid clientId; string cookieValue = context.Request.Cookies[COOKIE_NAME]; if (String.IsNullOrWhiteSpace(cookieValue) || !Guid.TryParse(cookieValue, out clientId)) { clientId = Guid.NewGuid(); context.Response.Cookies.Append(COOKIE_NAME, clientId.ToString()); } return clientId; } public void ReleaseClientId(Guid clientId, HttpContext context) { context.Response.Cookies.Delete(COOKIE_NAME); } } // Registration in Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddServerSentEvents(); services.AddServerSentEventsClientIdProvider<CookieBasedServerSentEventsClientIdProvider>(); }Restrict SSE endpoint access by authorization policies and claims
mainFor fine-grained control, such as checking for specific claims, you must first register a named policy using the standard ASP.NET Core
AddAuthorizationmethod. Once the policy is registered, apply it to the SSE endpoint by setting thePolicyproperty onServerSentEventsAuthorizationto the name of your policy.// 1. Register the policy in ConfigureServices services.AddAuthorization(options => { options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber")); }); // ... // 2. Apply the policy in Configure app.MapServerSentEvents("/default-sse-endpoint", new ServerSentEventsOptions { Authorization = new ServerSentEventsAuthorization { Policy = "EmployeeOnly" } });