Movatterモバイル変換


[0]ホーム

URL:


CTRL K
    Documentation
    Content
    Handlers
    Server Sent Events (SSE)

    Server Sent Events (SSE)

    GenHTTP.Modules.ServerSentEvents

    Server Sent Events (SSE) allow client applications such asbrowsers to subscribe to a stream of relevant events without the overhead added by the bi-directional connectionof websockets. Therefore they are useful whenever the server would like to notify the client about changes. This articledescribes how to host an event source in C# using the GenHTTP web server.

    ℹ️
    Event sources can quickly be created by using aproject template.

    Example

    The following example will host a SSE endpoint that will streamrandomly generated updates on stock prices to a client until it disconnects.

    using GenHTTP.Engine.Internal;using GenHTTP.Modules.IO;using GenHTTP.Modules.Layouting;using GenHTTP.Modules.Practices;using GenHTTP.Modules.ServerSentEvents;var client = Content.From(Resource.FromAssembly("Client.html"));var stocks = EventSource.Create()                        .Generator(GenerateStock);var app = Layout.Create()                .Add("stock", stocks)                .Index(client);await Host.Create()          .Handler(app)          .Defaults()          .Development()          .Console()          .RunAsync();staticasync ValueTask GenerateStock(IEventConnection connection){var rand =new Random();var stockSymbols =new List<string> {"AAPL","GOOGL","MSFT" };await connection.CommentAsync("Sending stock data");while (connection.Connected)    {var symbol = stockSymbols[rand.Next(0,3)];await connection.DataAsync(rand.Next(100,1000), symbol);await Task.Delay(1000);    }}

    For the client to be served, create a newClient.html in your project, mark itasEmbedded Resource and paste the following content:

    <!DOCTYPE html><htmllang="en"><head>    <metacharset="UTF-8">    <metaname="viewport"content="width=device-width, initial-scale=1.0">    <title>Stock Tracker</title>    <style>body {font-family: Arial,sans-serif;        }        #stocks {display:flex;flex-direction:column;gap:10px;margin-top:20px;        }        .stock {padding:10px;border:1pxsolid#ddd;border-radius:5px;background-color:#f9f9f9;        }    </style></head><body><h1>Stock Tracker</h1><divid="stocks"></div><script>// Establish a connection to the server using Server-Sent EventsconsteventSource=newEventSource('http://localhost:8080/stock/');// Function to display stock updatesfunctionupdateStock(symbol,value) {letstockElement= document.getElementById(symbol);if (!stockElement) {// Create a new element for the stock symbol if it doesn't existstockElement= document.createElement('div');stockElement.id=symbol;stockElement.className='stock';            document.getElementById('stocks').appendChild(stockElement);        }// Update stock valuestockElement.innerHTML=`<strong>${symbol}:</strong>${value}`;    }// Event listener for general updateseventSource.onmessage=function(event) {updateStock(event.type,event.data);    };// Event listeners for specific stock symbolsconstsymbols= ['AAPL','GOOGL','MSFT'];// Example stockssymbols.forEach(symbol => {eventSource.addEventListener(symbol,event => {updateStock(symbol,event.data);        });    });// Error handlingeventSource.onerror=function() {console.error('Connection to the server lost.');    };</script></body></html>

    After running this sample and navigating to http://localhost:8080 in your browser,you will see a simple web application consuming the generated events:

    Client application consuming the SSE stream

    Sending Data

    TheGenerator passed to theEventSource receives anIEventConnection thatallows you to interact with the connected client. As the specification allowsstring messages only, there are some convenience methods to format .NET typeson the wire.

    EventSource.Create()           .Generator(async (connection) => {await connection.CommentAsync("this is a comment");await connection.DataAsync("string message");// formatted to stringawait connection.DataAsync(42.1);// serialized to JSONawait connection.DataAsync(new List<string>() {"4711" });           });

    The formatters support basic types such asint,float,bool orDateOnly. You can customize thisbehavior by adding a custom formatter registry to the source:

    var registry = Formatting.Default()                         .Add(new MyFormat());var eventSource = EventSource.Create()                             .Formatting(registry)                             .Generator(...);

    Event Types

    When sending data, you can optionally specify a custom chosen eventtype. This allows clients to subscribe to the events they are interested in.

    await connection.DataAsync("user xyz logged in", eventType:"UserLogin");

    Event IDs

    Event IDs allow you to establish a protocol where the client can tell which eventsthey already consumed and the server resumes the event stream from this point on.

    await connection.DataAsync("some event", eventId:"4711");

    With this information, the client can specify theLast-Event-ID they consumed using aHTTP header field when connecting to the server. This information is madeavailable viaconnection.LastEventId.

    Parameters

    TheIEventConnection provides access to the underlying HTTP request viaconnection.Request, soyou can use query parameters or the request path to pass arguments to your logic:

    // http://localhost:8080/events?user=123var userId = connection.Request.Query["user"];// http://localhost:8080/events/typevar eventType = connection.Request.Target.Current.Value;

    Connection Lifecycle

    Most scenarios will keep the connection to a client open until it disconnects from their side.This implies some loop that can be achieved using theConnected property of the connection:

    while (connection.Connected){// wait for data and generate events}

    Please note that the server will only recognize that a client is no longerconnected when it tries to send data. Therefore, all of theData methods willreturn a boolean value indicating whether the operation succeeded or not. If an operationfails, theConnected property will be set tofalse allowing your loop to exit on thenext evaluation.

    If the server has no events to be sent to the client, it is recommended by the specification toperiodically send comments to prevent proxy servers from cancelling the running request and toverify that the client is still connected.

    If the server has no data for a connecting client and will most likely not produce any, the servercan indicate this to the client by sending a204 No Content response. This way the server does nothave to maintain an open connection that will not be used for communication. For this purpose, theAPI allows to pass anInspector that will be called before the generator is invoked to produceevents.

    EventSource.Create()           .Inspector(Inspect)           .Generator(...);private ValueTask<bool> Inspect(IRequest request,string? lastEventId){// return false to close the connection and instruct the client no to connect againreturnnew(false);}

    If a source system required to generate events is not available or it is clear that there willbe no events for the client in the near future, you can also instruct the client to reconnect at alater point in time:

    // instructs the client to reconnect in five minutesawait connection.RetryAsync(new(0,5,0));

    After a retry message has been sent, the connection can no longer be used to send any messages andshould be closed immediately by exiting the generator delegate.

    Error Handling

    By default, theEventSource handler will catch any exception that occurs in the generator logic, logit to the server companion and instruct the client to reconnect after 10 seconds. As the HTTP headers for theevent stream have already been sent, there is no general mechanism to inform the client about errors. Thiscan be achieved by adding your owntry/catch block to your generator:

    try{// generate events}catch (Exception e){// log the error, inform the client, and instruct them to reconnect// put your custom error handling logic here    Console.WriteLine(e);await connection.DataAsync(e.Message, eventType:"OnError");await connection.RetryAsync(new(0,0,10));}

    Share


    Reverse ProxiesStatic Resources

    [8]ページ先頭

    ©2009-2025 Movatter.jp