ASPNET核心服务器发送事件/响应刷新

Zyg*_*tas 14 c# asp.net-core-mvc asp.net-core asp.net-core-1.0

虽然没有官方文档,但是有谁知道如何使用ASP.NET Core实现SSE?

我怀疑一个实现可能会使用自定义中间件,但也许可以在控制器操作中执行此操作?

Sha*_*tin 41

wwwroot文件/ index.html的

在页面加载时,EventSourcehttp://www.somehost.ca/sseURL 创建一个.然后将其事件写入控制台.

<body>
    <script type="text/javascript">

        var source = new EventSource('sse');

        source.onmessage = function (event) {
            console.log('onmessage: ' + event.data);
        };

        source.onopen = function(event) {
            console.log('onopen');
        };

        source.onerror = function(event) {
            console.log('onerror');
        }

    </script>
</body>
Run Code Online (Sandbox Code Playgroud)

中间件

中间件处理sse路径.它将Content-Type标头设置为text/event-stream服务器套接字事件所需的标头.它写入响应流,而不关闭连接.它通过在写入之间延迟五秒来模仿工作.

app.Use(async (context, next) =>
{
    if (context.Request.Path.ToString().Equals("/sse"))
    {
        var response = context.Response;
        response.Headers.Add("Content-Type", "text/event-stream");

        for(var i = 0; true; ++i)
        {
            // WriteAsync requires `using Microsoft.AspNetCore.Http`
            await response
                .WriteAsync($"data: Middleware {i} at {DateTime.Now}\r\r");

            await response.Body.FlushAsync();
            await Task.Delay(5 * 1000);
        }
    }

    await next.Invoke();
});
Run Code Online (Sandbox Code Playgroud)

调节器

控制器完成与中间件完全相同的操作.

[Route("/api/sse")]
public class ServerSentEventController : Controller
{
    [HttpGet]
    public async Task Get()
    {
        var response = Response;
        response.Headers.Add("Content-Type", "text/event-stream");

        for(var i = 0; true; ++i)
        {
            await response
                .WriteAsync($"data: Controller {i} at {DateTime.Now}\r\r");

            response.Body.Flush();
            await Task.Delay(5 * 1000);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Firefox中的控制台输出

这是Firefox控制台窗口中的结果.每五秒钟就有一条新消息到来.

onopen
onmessage: Message 0 at 4/15/2016 3:39:04 PM
onmessage: Message 1 at 4/15/2016 3:39:09 PM
onmessage: Message 2 at 4/15/2016 3:39:14 PM
onmessage: Message 3 at 4/15/2016 3:39:19 PM
onmessage: Message 4 at 4/15/2016 3:39:24 PM
Run Code Online (Sandbox Code Playgroud)

参考文献:


小智 17

服务器发送的事件可以完全在控制器操作中实现。

这是基于 Shaun Luttin 的回答,但它更像是一个真实世界的例子,因为它将无限期地保持打开连接,并且它会向 发送消息以EventSource响应正在创建的消息。

using Example.Models;
using Example.Repositories;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Example.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class SseMessagesController : ControllerBase
    {
        private readonly IMessageRepository messageRepository;
        private readonly JsonSerializerSettings jsonSettings;

        public SseMessagesController(IMessageRepository messageRepository)
        {
            this.messageRepository = messageRepository;
            this.jsonSettings = new JsonSerializerSettings();
            jsonSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        }

        [HttpGet]
        public async Task GetMessages(CancellationToken cancellationToken)
        {
            Response.StatusCode = 200;
            Response.Headers.Add("Content-Type", "text/event-stream");

            EventHandler<MessageCreatedArgs> onMessageCreated = async (sender, eventArgs) =>
            {
                try
                {
                    var message = eventArgs.Message;
                    var messageJson = JsonConvert.SerializeObject(message, jsonSettings);
                    await Response.WriteAsync($"data:{messageJson}\n\n");
                    await Response.Body.FlushAsync();
                }
                catch (Exception)
                {
                    // TODO: log error
                }
            };
            messageRepository.MessageCreated += onMessageCreated;

            while (!cancellationToken.IsCancellationRequested) {
                await Task.Delay(1000);
            }

            messageRepository.MessageCreated -= onMessageCreated;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

每当EventSource连接到 时/api/ssemessages,我们都会MessageCreated向消息存储库上的事件添加一个事件委托。然后我们每 1 秒检查一次EventSource是否已经关闭,这会导致请求被取消。一旦请求被取消,我们将删除事件委托。

事件委托Message从事件参数中获取对象,将其序列化为 JSON(使用驼峰式大小写与 ASP.NET Core 在返回对象结果时的默认行为一致),将 JSON 写入正文,并刷新正文的流进行推送数据到EventSource.

有关创建事件委托的更多信息,请参阅本文.NET Core 的此更新

此外,如果您在 Nginx 后面托管它,您将需要阅读这个 SO answer这个 ServerFault answer

  • 您是否有包含“IMessageRepository”实现的完整示例的 GitHub 存储库或要点? (3认同)
  • @MarkG 我知道已经是 1 年后了,但这可能会有所帮助:https://github.com/Nordes/Sample.SSEvent (我不认为我会删除它,但它可以帮助您了解事件即将发生的地方来自以及如何广播)。 (3认同)