C# MVCでServer-Sent Eventsを実装する(ApiControllerサンプル)

(クライアント側)JavaScriptコード:

// サーバーイベントストリームの接続処理
$(function () {
    if (typeof (EventSource) !== "undefined") {
        // メッセージ表示領域の指定
        const outputArea = document.getElementById("EventOutput");
        // イベントストリームの初期化
        const stream = new EventSource("/api/EventStream/InitConnection");
        // 接続確立時の処理
        stream.onopen = function (e) {
            outputArea.innerHTML += "接続成功<br/>";
        }
        // メッセージ受信時の処理
        stream.onmessage = function (e) {
            outputArea.innerHTML += e.data + "<br/>";
        };
        // エラー発生時の処理
        stream.onerror = function (e) {
            outputArea.innerHTML += "接続エラー<br/>";
            stream.close();
        }
    }
    else {
        console.log("ブラウザがイベントストリームに対応していません!");
    }
})

(サーバー側)C# MVCコントローラコード:

using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web.Http;

namespace CabinetMS.Controllers.Api
{

    /// <summary>
    /// イベントストリームメッセージの定義
    /// </summary>
    public class ServerMessageData
    {
        public string MessageId { get; set; }
        public string Content { get; set; }
    }



    /// <summary>
    /// イベントストリーム接続処理
    /// </summary>
    [RoutePrefix("api/EventStream")]
    public class EventStreamController : ApiController
    {

        // クライアント接続処理
        [HttpGet, Route("InitConnection")]
        public HttpResponseMessage InitConnection(HttpRequestMessage request)
        {
            var response = request.CreateResponse();
            response.Content = new PushStreamContent((Action<Stream, HttpContent, TransportContext>)InitializeStream, new MediaTypeHeaderValue("text/event-stream"));
            return response;
        }

        private static readonly ConcurrentDictionary<StreamWriter, StreamWriter> activeStreams = new ConcurrentDictionary<StreamWriter, StreamWriter>();
        public void InitializeStream(Stream output, HttpContent content, TransportContext context)
        {
            var writer = new StreamWriter(output);
            activeStreams.TryAdd(writer, writer);
        }

        // クライアントへのメッセージ送信メソッド
        public static void SendClientMessage(ServerMessageData message)
        {
            BroadcastMessage(message);
        }

        // メッセージ送信処理
        private static void BroadcastMessage(ServerMessageData message)
        {
            foreach (var connection in activeStreams.ToArray())
            {
                try
                {
                    connection.Value.WriteLine($"id: {message.MessageId}\n");
                    connection.Value.WriteLine($"data: {message.Content}\n\n");
                    connection.Value.Flush();
                }
                catch
                {
                    StreamWriter disconnected;
                    activeStreams.TryRemove(connection.Value, out disconnected);
                }
            }
        }
    }
}

(サーバー側)ストリーム確立後のメッセージ送信例:

// クライアントへ通知送信
var notification = new ServerMessageData();
notification.MessageId = "ALERT-001";
notification.Content = "システム通知メッセージ";
EventStreamController.SendClientMessage(notification);

タグ: C# ASP.NET MVC javascript Server-Sent Events

7月5日 18:37 投稿