C# MVC 实现Server-Sent Events(ApiController案例)

(浏览器端) JS代码:

// 建立ServerSentEvents(服务器向浏览器推送信息,简称SSE)
    $(function () {
        if (typeof (EventSource) !== "undefined") {
            // 展示服务器推送内容的DOM
            var container = document.getElementById("SseContainer");
            // 建立SSE通道
            var es = new EventSource("/api/ServerSentEvents/BuildingSse");
            // 监听SSE通道open事件
            es.onopen = function (event) {
                container.innerHTML += "open<br/>";
            }
            // 监听SSE接收到的服务端消息
            es.onmessage = function (event) {
                container.innerHTML += event.data + "<br/>";
            };
            // 监听SSE通道error事件
            es.onerror = function (event) {
                container.innerHTML += "error<br/>";
                es.close();
            }
        }
        else {
            console.log("浏览器不支持ServerSentEvents接口!");
        }
    })

 

(服务器端)C# MVC Controller代码:

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.WebApi
{

    /// <summary>
    /// 自定义的SSE消息对象实体
    /// </summary>
    public class SseMessageObject
    {
        public string MsgId { get; set; }
        public string MsgData { get; set; }
    }



    /// <summary>
    /// 以请求方式建立SSE通道
    /// </summary>
    [RoutePrefix("api/ServerSentEvents")]
    public class ServerSentEventController : ApiController
    {

        // 接收浏览器请求,建立ServerSentEvents通道
        [HttpGet, Route("BuildingSse")]
        public System.Net.Http.HttpResponseMessage BuildingSse(HttpRequestMessage request)
        {
            System.Net.Http.HttpResponseMessage response = request.CreateResponse();
            response.Content = new System.Net.Http.PushStreamContent((Action<Stream, HttpContent, TransportContext>)WriteToStream, new MediaTypeHeaderValue("text/event-stream"));
            return response;
        }

        private static readonly ConcurrentDictionary<StreamWriter, StreamWriter> _streammessage = new ConcurrentDictionary<StreamWriter, StreamWriter>();
        public void WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
        {
            StreamWriter streamwriter = new StreamWriter(outputStream);
            _streammessage.TryAdd(streamwriter, streamwriter);
        }

        // 建立SSE通道后,其他Controller或程序调用此方法,可以向浏览器端主动推送消息
        public static void SendSseMsg(SseMessageObject sseMsg)
        {
            MessageCallback(sseMsg);
        }

        // 设置向浏览器推送的消息内容
        private static void MessageCallback(SseMessageObject sseMsg)
        {
            foreach (var subscriber in _streammessage.ToArray())
            {
                try
                {
                    subscriber.Value.WriteLine(string.Format("id: {0}\n", sseMsg.MsgId));
                    subscriber.Value.WriteLine(string.Format("data: {0}\n\n", sseMsg.MsgData));
                    subscriber.Value.Flush();
                }
                catch
                {
                    StreamWriter streamWriter;
                    _streammessage.TryRemove(subscriber.Value, out streamWriter);
                }
            }
        }
    }
}

 

(服务器端)成功建立SSE通道后,向浏览器推送消息:

// 服务端向网页端推送告警信息
var sseMsg = new SseMessageObject();
sseMsg.MsgId = "1101";
sseMsg.MsgData = "自定义告警消息";
ServerSentEventController.SendSseMsg(sseMsg);

 

posted @ 2020-09-27 18:45  最好的年纪  阅读(867)  评论(0编辑  收藏  举报