• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
PowerCoder
博客园    首页    新随笔    联系   管理    订阅  订阅

在ASP.NET Core中发送EventSource消息给前端页面

EventSource 是一个用于服务器推送事件(Server-Sent Events, SSE)的接口,它允许服务器推送实时更新到浏览器。与 WebSocket 不同,SSE 是单向的(服务器到客户端),适用于更新频率不高的实时通知、消息推送等场景。

下面我们使用一个ASP.NET Core MVC(ASP.NET Core MVC 8.0)项目,来展示如何在ASP.NET Core中定义EventSource的接口。

 

首先我们在ASP.NET Core的Program.cs文件中,开启了AllowSynchronousIO = true的配置,原因如下面代码中的注释所述:

using Microsoft.AspNetCore.Server.Kestrel.Core;

namespace AspNetCoreEventSource
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddControllersWithViews();

            // 因为在HomeController的EventSource方法中,使用了StreamWriter.Write同步方法向Response.Body中写入数据,所以要在ASP.NET Core中开启AllowSynchronousIO = true配置,否则会抛出异常:
            // System.InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.
            builder.Services.Configure<KestrelServerOptions>(x => x.AllowSynchronousIO = true)
            .Configure<IISServerOptions>(x => x.AllowSynchronousIO = true);

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");

            app.Run();
        }
    }
}

 

然后,我们在HomeController中定义了一个EventSource接口,来向前端发送EventSource的数据:

using Microsoft.AspNetCore.Mvc;
using System.Text;

namespace AspNetCoreEventSource.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public IActionResult Index()
        {
            return View();
        }

        /// <summary>
        /// EventSource服务端接口
        /// 默认情况下,前端页面JavaScript原生的EventSource类只支持HTTP Get方式的接口,如果要使用HTTP Post方式的接口来发送HTTP Body,前端要使用第三方框架(例如:Fetch Event Source)
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task EventSource()
        {
            Response.ContentType = "text/event-stream";
            Response.Headers.Append("Pragma", "no-cache");

            using (StreamWriter sw = new StreamWriter(Response.Body, Encoding.UTF8))
            {
                for (int i = 0; i < 10; i++)
                {
                    // 必须是"data:数据\n\n"这种格式
                    sw.Write("data:" + "这是消息 " + (i + 1).ToString() + "\n\n");
                    sw.Flush();

                    await Task.Delay(1000);
                }

                // 前端接收到数据等于:[stream-end]代表结束
                sw.WriteLine("data:[stream-end]\n\n");
            }
        }
    }
}

如上面代码中的注释所述,默认情况下,前端页面JavaScript原生的EventSource类只支持HTTP Get方式的接口,如果要使用HTTP Post方式的接口来发送HTTP Body,前端要使用第三方框架(例如:Fetch Event Source)

 

最后,我们给HomeController的Index方法,定义一个MVC的视图页面Index.cshtml,来使用JavaScript原生的EventSource类调用HomeController的EventSource接口,然后将收到的消息都显示在页面上:

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <script>
        function ReceiveEvent() {
            const source = new EventSource('@Url.Action("EventSource")');

            source.onmessage = function (event) {
                if (event.data != '[stream-end]') {
                    if (event.data) {
                        document.getElementById('content').innerHTML += event.data + '<br/>';
                    }
                    else {
                        document.getElementById('content').innerHTML += '<br/>';
                    }
                }
                else {
                    source.close();
                    console.log('请求结束');
                }
            }
        }
    </script>
</head>
<body>
    <div>
        <button id="btnSend" onclick="ReceiveEvent()" >发送消息</button>
    </div>
    <div id="content" style="width:800px;height:600px;overflow:scroll;">

    </div>
</body>
</html>

 

运行ASP.NET Core项目,点击页面上的”发送消息“按钮,就可以通过EventSource来收到服务端接口发过来的每条消息:

 

参考文献:

SSE、EventSource了解

EventSource 在项目中常用的两种方式

 

posted @ 2025-04-11 02:26  PowerCoder  阅读(163)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3