Skip to content
Go back

ASP.NET Core 与 .NET 10 中的实时 Server-Sent Events 技术详解

Published:  at  12:00 AM

ASP.NET Core 与 .NET 10 中的实时 Server-Sent Events 技术详解

随着实时 Web 应用场景日益丰富,如何高效地将后端变动第一时间推送到前端,已成为现代 .NET 应用开发中绕不开的话题。ASP.NET Core 10 开始,官方正式引入了原生 Server-Sent Events(SSE)支持。本文将围绕 SSE 技术,从原理、ASP.NET Core 实现、重连机制、前端消费、与 SignalR 对比等多个维度,深入解析 SSE 在实时推送领域的价值与落地实践。

什么是 Server-Sent Events(SSE)?

Server-Sent Events(SSE)是一种基于 HTTP 协议的单向推送技术,允许服务器主动向浏览器等客户端连续推送实时数据,而无需客户端反复轮询。它使用 text/event-stream 作为响应类型,在浏览器端由原生 EventSource API 直接支持。SSE 适用于股票行情、系统通知、实时监控等一切需要“服务端到客户端单向流式数据”的场景。

SSE 的核心特点包括:

SSE 现已在 Chrome、Firefox、Safari、Edge 等主流浏览器实现,并可通过 curl、Postman 等工具直接测试。

常见应用场景

SSE 适合但不限于以下场景:

其实现门槛远低于 WebSocket/SignalR,尤其适用于无需客户端反馈的实时可视化与播报类应用。

ASP.NET Core 10 中的 SSE 实现

从 .NET 10 Preview 4 开始,ASP.NET Core 增加了对 SSE 的内置支持。开发者只需将接口的 Content-Type 设置为 text/event-stream,利用 Minimal API 方式即可实现高性能 SSE 推送。

1. 服务端异步流数据生成

例如,创建一个每隔两秒推送一次股票报价的 StockService:

public record StockPriceEvent(string Id, string Symbol, decimal Price, DateTime Timestamp);

public class StockService
{
    public async IAsyncEnumerable<StockPriceEvent> GenerateStockPrices(
        [EnumeratorCancellation] CancellationToken cancellationToken)
    {
        var symbols = new[] { "MSFT", "AAPL", "GOOG", "AMZN" };

        while (!cancellationToken.IsCancellationRequested)
        {
            var symbol = symbols[Random.Shared.Next(symbols.Length)];
            var price = Math.Round((decimal)(100 + Random.Shared.NextDouble() * 50), 2);
            var id = DateTime.UtcNow.ToString("o");

            yield return new StockPriceEvent(id, symbol, price, DateTime.UtcNow);

            await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken);
        }
    }
}

2. SSE Endpoint 配置

利用 Minimal API,可以快速实现 SSE 推送接口:

builder.Services.AddSingleton<StockService>();

app.MapGet("/stocks", (StockService stockService, CancellationToken ct) =>
{
    return TypedResults.ServerSentEvents(
        stockService.GenerateStockPrices(ct),
        eventType: "stockUpdate"
    );
});

这样,前端每两秒即可收到一条最新的股票数据,且通道为持久长连,无需额外配置。

SSE 的断线重连与 Last-Event-ID 机制

SSE 天然支持断线重连功能。浏览器会自动带上 Last-Event-ID 头,指明上次接收到的事件 ID,方便服务端判断断点续传。例如:

Last-Event-ID: 20250616T150430Z

在 ASP.NET Core 端,可以这样获取并处理重连逻辑:

app.MapGet("/stocks2", (
    StockService stockService,
    HttpRequest httpRequest,
    CancellationToken ct) =>
{
    var lastEventId = httpRequest.Headers.TryGetValue("Last-Event-ID", out var id)
        ? id.ToString()
        : null;

    if (!string.IsNullOrEmpty(lastEventId))
    {
        app.Logger.LogInformation("Reconnected, client last saw ID {LastId}", lastEventId);
    }

    var stream = stockService.GenerateStockPricesSince(lastEventId, ct)
        .Select(evt =>
        {
            var sseItem = new SseItem<StockPriceEvent>(evt, "stockUpdate")
            {
                EventId = evt.Id
            };
            return sseItem;
        });

    return TypedResults.ServerSentEvents(
        stream,
        eventType: "stockUpdate"
    );
});

这不仅提升了用户体验,也保证了消息传递的完整性与连续性。

开发环境下的 CORS 支持

前端跨域访问 SSE 接口时,需在开发环境中允许 CORS:

if (builder.Environment.IsDevelopment())
{
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowFrontend", policy =>
        {
            policy.WithOrigins("*")
                .AllowAnyHeader()
                .AllowAnyMethod();
        });
    });
}

if (app.Environment.IsDevelopment())
{
    app.UseCors("AllowFrontend");
}

前端消费 SSE:EventSource 实践

前端使用原生的 EventSource API 即可轻松对接后端 SSE 服务:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Live Stock Ticker</title>
    <script src="https://cdn.tailwindcss.com"></script>
  </head>
  <body class="min-h-screen bg-gray-50 p-8">
    <div class="mx-auto max-w-4xl">
      <h1 class="mb-6 flex items-center text-3xl font-bold text-gray-800">
        📈<span class="ml-2">Live Stock Market Updates</span>
      </h1>
      <div class="rounded-lg bg-white p-6 shadow-md">
        <ul id="updates" class="divide-y divide-gray-200"></ul>
      </div>
    </div>
    <script>
      const source = new EventSource("http://localhost:5000/stocks");

      source.addEventListener("stockUpdate", e => {
        const { symbol, price, timestamp } = JSON.parse(e.data);

        const li = document.createElement("li");
        li.classList.add("new", "flex", "justify-between", "items-center");

        const timeSpan = document.createElement("span");
        timeSpan.classList.add("text-gray-500", "text-sm");
        timeSpan.textContent = new Date(timestamp).toLocaleTimeString();

        const symbolSpan = document.createElement("span");
        symbolSpan.classList.add("font-medium", "text-gray-800");
        symbolSpan.textContent = symbol;

        const priceSpan = document.createElement("span");
        priceSpan.classList.add("font-bold", "text-green-600");
        priceSpan.textContent = `$${price}`;

        li.appendChild(timeSpan);
        li.appendChild(symbolSpan);
        li.appendChild(priceSpan);

        const list = document.getElementById("updates");
        list.prepend(li);

        setTimeout(() => li.classList.remove("new"), 2000);
      });

      source.onerror = err => {
        console.error("SSE connection error:", err);
      };
    </script>
  </body>
</html>

每当服务端有新推送,页面便会实时更新行情列表,完全免除轮询和手动刷新。

实时股票行情效果图

测试 SSE 接口:IDE HTTP 文件与网络调试

现代 IDE(如 Visual Studio Code、JetBrains Rider)均支持 HTTP Request 文件,直接编写如下内容即可调试 SSE 接口:

@ServerSentEvents_HostAddress = http://localhost:5000

GET {{ServerSentEvents_HostAddress}}/stocks
Accept: text/event-stream

在浏览器 DevTools 的 Network 面板中,也可直观查看事件流数据:

浏览器网络面板调试 SSE

SSE 与 SignalR(WebSocket)的核心区别

SSE 与 SignalR 虽同为实时通信技术,但设计理念与应用场景存在明显差异。对比如下:

维度SSESignalR (WebSocket)
协议HTTP/1.1(text/event-stream)WebSocket/HTTP(多种回退)
通信方向单向(服务器→客户端)双向全双工
浏览器支持原生支持,兼容广泛需 WebSocket 支持,部分回退
资源消耗低,仅单一长连接高,需专门帧管理
消息类型仅文本文本/二进制
API 丰富度简单 API,易上手丰富,支持分组、身份、广播等
适用场景通知推送、状态流、日志聊天、协作、需要客户端上传数据等
扩展与弹性按普通 HTTP 扩展支持分布式扩展,如 Redis Backplane

综上,SSE 更适用于“单向消息推送”与流式广播;而 SignalR 则主打“实时互动”与高阶分布式场景。实际选择应结合业务需求与技术栈定制。

总结与最佳实践

ASP.NET Core 10 的 SSE 支持,让 .NET 开发者能更低门槛地构建实时推送应用。其简洁、易用、资源友好的特性,在各类数据流、仪表盘、实时监控等场景表现优异。当只需服务端到客户端单向推送时,推荐优先考虑 SSE;如需双向通信或复杂分组、分布式广播,则 SignalR 更合适。

感兴趣的开发者可下载完整源码进行实践,结合自身业务落地实时能力,提升应用交互体验与实时性。


原文作者:Anton Martyniuk

原文链接:https://antondevtips.com/blog/real-time-server-sent-events-in-asp-net-core



Previous Post
Angular 20.2 稳定发布 Zoneless 模式,Signals 全面接管异步变更检测
Next Post
利用Claude驱动的AI代码安全审查GitHub Action实战解析