【问题标题】:server sent events (eventsource) with standard asp.net mvc causing error服务器使用标准 asp.net mvc 发送事件(事件源)导致错误
【发布时间】:2016-06-08 12:01:32
【问题描述】:

我正在尝试处理服务器发送的事件,但我无法让它在 MVC 项目(不是 WebAPI)中工作。我在网上没有找到任何好的样品。

这是我尝试过的服务器端代码(包括来自不同帖子的几次失败尝试):

Function GetRows() as ActionResult
    Dim ret = New HttpResponseMessage
    ' ret.Content.w
    ' Return ret

    Response.ContentType = "text/event-stream"
    Response.Write("data: " & "aaaa")
    Dim flag = False
    If flag Then
        For i = 0 To 100
            Response.Write("data: " & i)
        Next
    End If
    Response.Flush()
    'Return Content("Abv")
    'Return Content(("data: {0}\n\n" & DateTime.Now.ToString(), "text/event-stream")
End Function

这里是 Javascript

var source = new EventSource("/Tool/GetRows");
source.onmessage = function (event) {
    document.getElementById("messages").innerHTML += event.data + "<br>";
};
source.onerror = function (e) {
    console.log(e);
};

由于某种原因,它总是进入onerror,并且没有任何信息表明它可能是什么类型的错误。

我做错了什么?

顺便说一句,我不认为这个动作真的应该返回任何东西,因为我的理解是它应该只是一个字符串一个字符串地写入流。

【问题讨论】:

    标签: asp.net-mvc vb.net server-sent-events eventsource


    【解决方案1】:

    EventSource expects a specific format,如果流与该格式不匹配,将引发 onerror - 一组由 : 分隔的字段/值对行,每行以换行符结尾:

    field: value
    field: value
    

    field 可以是dataeventidretry 之一,或者为空以表示评论(将被 EventSource 忽略)。

    data 可以跨越多行,但每一行都必须以 data: 开头

    每个事件触发器都必须以双换行符结尾

    data: 1
    data: second line of message
    
    data: 2
    data: second line of second message
    

    注意:如果你是用VB.NET编写的,你不能使用\n转义序列来写换行符;你必须使用vbLfChr(10)


    顺便说一句,EventSource 应该保持与服务器的开放连接。来自MDN(强调我的):

    EventSource 接口用于接收服务器发送的事件。它通过 HTTP 连接到服务器并以文本/事件流格式接收事件而不关闭连接

    一旦控制从 MVC 控制器方法中退出,结果将被打包并发送给客户端,并关闭连接。 EventSource 的一部分是客户端将尝试重新打开连接,该连接将再次被服务器立即关闭;由此产生的close -&gt; reopen循环也可以看到here

    该方法应该有某种循环,而不是退出该方法,该循环将持续写入Response 流。


    VB.NET 中的示例

    Imports System.Threading
    Public Class HomeController
        Inherits Controller
    
        Sub Message()
            Response.ContentType= "text/event-stream"
            Dim i As Integer
            Do
                i += 1
                Response.Write("data: DateTime = " & Now & vbLf)
                Response.Write("data: Iteration = " & i & vbLf)
                Response.Write(vbLf)
                Response.Flush
    
                'The timing of data sent to the client is determined by the Sleep interval (and latency)
                Thread.Sleep(1000) 
            Loop
        End Sub
    End Class
    

    C# 中的示例

    客户端:

    <input type="text" id="userid" placeholder="UserID" /><br />
    <input type="button" id="ping" value="Ping" />
    
    <script>
        var es = new EventSource('/home/message');
        es.onmessage = function (e) {
            console.log(e.data);
        };
        es.onerror = function () {
            console.log(arguments);
        };
    
        $(function () {
            $('#ping').on('click', function () {
                $.post('/home/ping', {
                    UserID: $('#userid').val() || 0
                });
            });
        });
    </script>
    

    服务器端:

    using System;
    using System.Collections.Concurrent;
    using System.Threading;
    using System.Web.Mvc;
    using Newtonsoft.Json;
    
    namespace EventSourceTest2.Controllers {
        public class PingData {
            public int UserID { get; set; }
            public DateTime Date { get; set; } = DateTime.Now;
        }
    
        public class HomeController : Controller {
            public ActionResult Index() {
                return View();
            }
    
            static ConcurrentQueue<PingData> pings = new ConcurrentQueue<PingData>();
    
            public void Ping(int userID) {
                pings.Enqueue(new PingData { UserID = userID });
            }
    
            public void Message() {
                Response.ContentType = "text/event-stream";
                do {
                    PingData nextPing;
                    if (pings.TryDequeue(out nextPing)) {
                        Response.Write("data:" + JsonConvert.SerializeObject(nextPing, Formatting.None) + "\n\n");
                    }
                    Response.Flush();
                    Thread.Sleep(1000);
                } while (true);
            }
        }
    }
    

    【讨论】:

    • 感谢 zev 提供清晰详细的说明。它现在工作了!
    猜你喜欢
    • 2020-06-20
    • 2021-02-19
    • 1970-01-01
    • 1970-01-01
    • 2020-03-06
    • 1970-01-01
    • 1970-01-01
    • 2013-12-05
    • 2012-04-22
    相关资源
    最近更新 更多